16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-22 02:29:17 +02:00

merge manul

This commit is contained in:
WLs50
2025-03-09 15:36:11 +01:00
parent a687f551a2
commit a5f6397eca
1211 changed files with 483542 additions and 58643 deletions

1
manul/.clang-format Normal file
View File

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

101
manul/CMakeLists.txt Normal file
View File

@@ -0,0 +1,101 @@
set(LIBMANUL_NAME "manul")
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")
# 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")
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")
target_link_libraries(${LIBMANUL_NAME} ${LIBMANUL_NAME}_fsr2 nvrhi yaml-cpp fmt EnTT glfw)

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 Manul Renderer");
return nullptr;
}
} // namespace
bool register_manul_d3d12 =
LIBMANUL_WITH_D3D12 &&
gfx_renderer_factory::get_instance()->register_backend(
"manul_d3d12", &create_nvrenderer_for_d3d12);
bool register_manul_vulkan =
LIBMANUL_WITH_VULKAN &&
gfx_renderer_factory::get_instance()->register_backend(
"manul_vulkan", &create_nvrenderer_for_vulkan);
bool register_manul = gfx_renderer_factory::get_instance()->register_backend(
"manul", &create_nvrenderer_default);

View File

@@ -0,0 +1,729 @@
#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::geometrybank_handle const &Geometry,
int const Type) override;
virtual gfx::geometry_handle Insert(gfx::vertex_array &Vertices,
gfx::geometrybank_handle const &Geometry,
int const Type) override;
virtual bool Replace(gfx::vertex_array &Vertices,
gfx::geometry_handle const &Geometry, int const Type,
std::size_t const Offset = 0) override;
virtual bool Append(gfx::vertex_array &Vertices,
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;
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,54 @@
#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);
}
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,48 @@
#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;
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,932 @@
#include "environment.h"
#include <fmt/format.h>
#include <nvrhi/utils.h>
#include "gbuffer.h"
#include "nvrenderer/nvrenderer.h"
#include "nvrendererbackend.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 = 2048;
int dynamic_skybox_width = 512;
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 = 1024;
int dynamic_specular_width = 256;
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();
{
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;
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))
.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 = -Global.DayLight.direction;
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,139 @@
#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::vec4 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;
};
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,209 @@
#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));
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);
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))
.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;
constants.m_light_dir = glm::vec4(-daylight.direction, 0.f);
constants.m_light_color = glm::vec4(m_sky->CalcSunColor(), 1.);
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::vec4 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,336 @@
#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>
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("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 (IsNormalMapShader() && HasNormalMap()) {
return "legacy_normalmap";
}
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("2:") != m_texture_mapping.end();
}
bool MaterialAdapterLegacyMatFile::IsNormalMapShader() const {
return m_shader.find("normalmap") != std::string::npos ||
m_shader.find("parallax") != 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,94 @@
#pragma once
#include <memory>
#include <string_view>
#include <optional>
#include <unordered_map>
#include <glm/glm.hpp>
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 IsNormalMapShader() 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"));
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::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);
}
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,442 @@
#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(-Global.DayLight.direction, 32);
return simulation::Environment.light_intensity() *
LinearSrgbFromSpectralSamples(sun_spectral_irradiance *
transmittance) *
glm::exp(EXPOSURE);
}
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 = 128;
m_sky_height = 512;
m_lut_width = 64;
m_lut_height = 256;
m_lut_slices = 32;
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 = -Global.DayLight.direction;
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);
}

115
manul/renderer/source/sky.h Normal file
View File

@@ -0,0 +1,115 @@
#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;
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;
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

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__

1
manul/thirdparty/entt vendored Submodule

Submodule manul/thirdparty/entt added at 4d3e11645b

1
manul/thirdparty/fmt vendored Submodule

Submodule manul/thirdparty/fmt added at 9212ff6ca1

24
manul/thirdparty/fsr2/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,24 @@
set(fsr2_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/src/ffx-fsr2-api")
set(fsr2_shader_dir "${CMAKE_CURRENT_SOURCE_DIR}/src/shaders")
set(fsr2_include_dir "${CMAKE_CURRENT_SOURCE_DIR}/include")
file(GLOB src "${fsr2_src_dir}/*.cpp" "${fsr2_src_dir}/*.h")
file(GLOB_RECURSE src_vk "${fsr2_src_dir}/vk/*.cpp" "${fsr2_src_dir}/vk/*.h")
file(GLOB_RECURSE src_dx12 "${fsr2_src_dir}/dx12/*.cpp" "${fsr2_src_dir}/dx12/*.h")
add_library("${LIBMANUL_NAME}_fsr2" STATIC ${src})
if (LIBMANUL_WITH_VULKAN)
target_sources("${LIBMANUL_NAME}_fsr2" PRIVATE ${src_vk})
target_link_libraries("${LIBMANUL_NAME}_fsr2" PRIVATE Vulkan::Headers Vulkan::Vulkan)
endif ()
if (LIBMANUL_WITH_DX12)
target_sources("${LIBMANUL_NAME}_fsr2" PRIVATE ${src_dx12})
endif ()
target_compile_definitions("${LIBMANUL_NAME}_fsr2" PUBLIC FFX_GCC)
target_include_directories("${LIBMANUL_NAME}_fsr2" PRIVATE ${fsr2_src_dir} ${fsr2_shader_dir} ${fsr2_include_dir})
target_include_directories("${LIBMANUL_NAME}_fsr2" INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/src")

View File

@@ -0,0 +1,141 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (C) 2024 Advanced Micro Devices, Inc.
#
# 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.
# Function to compile a set of shaders using the FidelityFX shader compiler driver.
#
# EXECUTABLE Path to the FidelityFX SC tool executable.
# BASE_ARGS The base arguments for the compiler.
# API_BASE_ARGS API (DX12 or Vulkan) specific base args.
# PERMUTATION_ARGS Permutation set to compile for.
# SHADER_FILES A list of shader files to compile using the permutation matrix expanded from SC_PERMUTATION_ARGS.
# OUTPUT_PATH Path to store compiled shader output.
#
# Returns
# A list of header files generated by the FidelityFX Shader Compiler driver.
function(compile_shaders_with_depfile
EXECUTABLE BASE_ARGS API_BASE_ARGS
PERMUTATION_ARGS INCLUDES_ARGS
SHADER_FILES OUTPUT_PATH
PERMUTATION_OUTPUTS)
#message(STATUS "OUTPUT_PATH=${OUTPUT_PATH}")
#message(STATUS "EXE=${EXECUTABLE}")
#message(STATUS "BASE=${BASE_ARGS}")
#message(STATUS "API=${API_BASE_ARGS}")
#message(STATUS "PERMS=${PERMUTATION_ARGS}")
#message(STATUS "INCLUDES=${INCLUDES_ARGS}")
#message(STATUS "SHADER_FILES=${SHADER_FILES}")
# Figure out if this HLSL or GLSL
list(GET SHADER_FILES 0 FIRST_SHADER_FILE)
get_filename_component(SHADER_EXT "${FIRST_SHADER_FILE}" LAST_EXT)
string(TOUPPER "${SHADER_EXT}" SHADER_EXT)
set(INCLUDE_SPACE )
set(HLSL_WAVE64_ARGS )
set(HLSL_WAVE32_ARGS )
set(HLSL_16BIT_ARGS )
if (SHADER_EXT STREQUAL ".HLSL")
set(INCLUDE_SPACE " ")
set(HLSL_WAVE64_ARGS "-DFFX_PREFER_WAVE64=\"[WaveSize(64)]\"" "-DFFX_HLSL_SM=66" -T cs_6_6)
set(HLSL_WAVE32_ARGS "-DFFX_HLSL_SM=62" -T cs_6_2)
set(HLSL_16BIT_ARGS "-enable-16bit-types")
endif()
# GLSL requires no spaces: -I<include0> -I<include1> etc.
# HLSL requires spaces: -I <include0> -I <include1> etc.
set(COMPILE_INCLUDE_ARGS )
foreach(INC ${INCLUDES_ARGS})
list(APPEND COMPILE_INCLUDE_ARGS "-I${INCLUDE_SPACE}${INC}")
endforeach(INC)
if (FFX_API_BACKEND STREQUAL GDK_DESKTOP_X64)
set(FFX_GDK_OPTION -compiler=gdk.desktop.x64)
elseif (FFX_API_BACKEND STREQUAL GDK_SCARLETT_X64)
set(FFX_GDK_OPTION -compiler=gdk.scarlett.x64)
else()
set(FFX_GDK_OPTION )
endif()
foreach(PASS_SHADER ${SHADER_FILES})
get_filename_component(PASS_SHADER_FILENAME ${PASS_SHADER} NAME_WE)
get_filename_component(PASS_SHADER_TARGET ${PASS_SHADER} NAME_WLE)
set(WAVE32_PERMUTATION_HEADER ${OUTPUT_PATH}/${PASS_SHADER_TARGET}_permutations.h)
set(WAVE64_PERMUTATION_HEADER ${OUTPUT_PATH}/${PASS_SHADER_TARGET}_wave64_permutations.h)
set(WAVE32_16BIT_PERMUTATION_HEADER ${OUTPUT_PATH}/${PASS_SHADER_TARGET}_16bit_permutations.h)
set(WAVE64_16BIT_PERMUTATION_HEADER ${OUTPUT_PATH}/${PASS_SHADER_TARGET}_wave64_16bit_permutations.h)
# combine base and permutation args
set(SC_ARGS ${BASE_ARGS} ${API_BASE_ARGS} ${PERMUTATION_ARGS})
# Wave32
add_custom_command(
OUTPUT ${WAVE32_PERMUTATION_HEADER}
COMMAND ${EXECUTABLE} ${FFX_GDK_OPTION} ${SC_ARGS} -name=${PASS_SHADER_FILENAME} -DFFX_HALF=0 ${HLSL_WAVE32_ARGS} ${COMPILE_INCLUDE_ARGS} -output=${OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE32_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_PERMUTATION_HEADER})
# Wave64
add_custom_command(
OUTPUT ${WAVE64_PERMUTATION_HEADER}
COMMAND ${EXECUTABLE} ${FFX_GDK_OPTION} ${SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64 -DFFX_HALF=0 ${HLSL_WAVE64_ARGS} ${COMPILE_INCLUDE_ARGS} -output=${OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE64_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_PERMUTATION_HEADER})
# Wave32 16-bit
add_custom_command(
OUTPUT ${WAVE32_16BIT_PERMUTATION_HEADER}
COMMAND ${EXECUTABLE} ${FFX_GDK_OPTION} ${SC_ARGS} -name=${PASS_SHADER_FILENAME}_16bit -DFFX_HALF=1 ${HLSL_16BIT_ARGS} ${HLSL_WAVE32_ARGS} ${COMPILE_INCLUDE_ARGS} -output=${OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE32_16BIT_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE32_16BIT_PERMUTATION_HEADER})
# Wave64 16-bit
add_custom_command(
OUTPUT ${WAVE64_16BIT_PERMUTATION_HEADER}
COMMAND ${EXECUTABLE} ${FFX_GDK_OPTION} ${SC_ARGS} -name=${PASS_SHADER_FILENAME}_wave64_16bit -DFFX_HALF=1 ${HLSL_16BIT_ARGS} ${HLSL_WAVE64_ARGS} ${COMPILE_INCLUDE_ARGS} -output=${OUTPUT_PATH} ${PASS_SHADER}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${PASS_SHADER}
DEPFILE ${WAVE64_16BIT_PERMUTATION_HEADER}.d
)
list(APPEND PERMUTATION_OUTPUTS ${WAVE64_16BIT_PERMUTATION_HEADER})
endforeach(PASS_SHADER)
set(${PERMUTATION_OUTPUTS} ${PERMUTATION_OUTPUTS} PARENT_SCOPE)
endfunction()
# macro to add shader output files to a list for dependencies
macro (add_shader_output)
foreach (_SHADER_SRC ${ARGN})
#message (STATUS ${_SHADER_SRC})
list (APPEND FFX_SC_PERMUTATION_OUTPUTS "${_SHADER_SRC}")
endforeach()
set (FFX_SC_PERMUTATION_OUTPUTS ${FFX_SC_PERMUTATION_OUTPUTS} PARENT_SCOPE)
endmacro()

View File

@@ -0,0 +1,48 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (C) 2024 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
set(BLUR_BASE_ARGS
-reflection -deps=gcc -DFFX_GPU=1)
set(BLUR_PERMUTATION_ARGS
-DFFX_BLUR_OPTION_KERNEL_DIMENSION={3,5,7,9,11,13,15,17,19,21}
-DFFX_BLUR_OPTION_KERNEL_PERMUTATION={0,1,2})
set(BLUR_INCLUDE_ARGS
"${FFX_GPU_PATH}"
"${FFX_GPU_PATH}/blur")
if (NOT BLUR_SHADER_EXT)
set(BLUR_SHADER_EXT *)
endif()
file(GLOB BLUR_SHADERS
"shaders/blur/ffx_blur_pass.${BLUR_SHADER_EXT}")
# compile all the shaders
compile_shaders_with_depfile(
"${FFX_SC_EXECUTABLE}"
"${BLUR_BASE_ARGS}" "${BLUR_API_BASE_ARGS}" "${BLUR_PERMUTATION_ARGS}" "${BLUR_INCLUDE_ARGS}"
"${BLUR_SHADERS}" "${FFX_PASS_SHADER_OUTPUT_PATH}" BLUR_PERMUTATION_OUTPUTS)
# add the header files they generate to the main list of dependencies
add_shader_output("${BLUR_PERMUTATION_OUTPUTS}")

View File

@@ -0,0 +1,937 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BLUR_H
#define FFX_BLUR_H
//_____________________________________________________________/\_______________________________________________________________
//==============================================================================================================================
//
// [FFX BLUR] Blur
//
//==============================================================================================================================
//
/// @defgroup FfxGPUBlur FidelityFX BLUR
/// FidelityFX Blur GPU documentation
///
/// @ingroup FfxGPUEffects
//------------------------------------------------------------------------------------------------------------------------------
//
// ABOUT
// =====
// AMD FidelityFX Blur is a collection of blurring effects implemented on compute shaders, hand-optimized for maximum performance.
// FFX-Blur includes
// - Gaussian Blur w/ large kernel support (up to 21x21)
//
//==============================================================================================================================
//==============================================================================================================================
// BLUR SETUP
//==============================================================================================================================
/// FFX_BLUR_TILE_SIZE_Y: Tile Y dimensions that the local threadgroup will work on.
/// Note: each threadgroup is responsible for blurring a tile of the input image.
///
/// @ingroup FfxGPUBlur
#ifndef FFX_BLUR_TILE_SIZE_Y
#define FFX_BLUR_TILE_SIZE_Y 8
#endif
/// FFX_BLUR_TILE_SIZE_X: Tile X dimensions that the local threadgroup will work on.
/// Note: each threadgroup is responsible for blurring a tile of the input image.
///
/// @ingroup FfxGPUBlur
#ifndef FFX_BLUR_TILE_SIZE_X
#define FFX_BLUR_TILE_SIZE_X 8
#endif
/// FFX_BLUR_DISPATCH_Y: Y dimension of the Blur compute dispatch.
/// The compute dispatch on the CPU side uses this value when invoking Dispatch.
///
/// @ingroup FfxGPUBlur
#define FFX_BLUR_DISPATCH_Y 8
#ifndef FFX_CPU
/// FFX_BLUR_OPTION_KERNEL_DIMENSION needs to be defined by the client application
/// App should define e.g the following for 5x5 blur:
/// #define FFX_BLUR_OPTION_KERNEL_DIMENSION 5
///
/// @ingroup FfxGPUBlur
#ifndef FFX_BLUR_OPTION_KERNEL_DIMENSION
#error Please define FFX_BLUR_OPTION_KERNEL_DIMENSION
#endif
/// FFX_BLUR_KERNEL_RANGE is defined relative to FFX_BLUR_OPTION_KERNEL_DIMENSION
/// See ffx_blur_callbacks_*.h for details.
///
/// @ingroup FfxGPUBlur
#ifndef FFX_BLUR_KERNEL_RANGE
#error Please define FFX_BLUR_KERNEL_RANGE
#endif
//--------------------------------------------------------------------------------------
// BLUR CONFIG
//--------------------------------------------------------------------------------------
// hardcoded variants
#define BLUR_DEBUG_PREFILL_OUTPUT_CACHE_WITH_COLOR 0
#define BLUR_GROUPSHARED_MEMORY_SOA 0 // [Deprecated] improves LDS but too high traffic still
#define BLUR_GROUPSHARED_MEMORY_HALF 0 // [Deprecated] LOTS of LDS traffic (1x ds_read per channel), need to pack with FfxUInt32
#define BLUR_GROUPSHARED_MEMORY_PK_UINT 1 // 1x ds_read2st64_b32 for all three channels
#define BLUR_FP16_KERNEL_LOOPS 1 // use fp16 for kernel loop counters and lds indexing (increases VGPR due to sdwa)
#define BLUR_FP16_CLAMP 1 // ensure fp16 min/max is used for clamp()
// cpu-driven variants
#ifndef BLUR_ENABLE_INPUT_CACHE
#define BLUR_ENABLE_INPUT_CACHE 0 // currently only slows the algorithm :(
#endif
#ifndef BLUR_DISABLE_CLAMP
#define BLUR_DISABLE_CLAMP 0 // Generates incorrect image at the image borders (no clamp), for testing theoretical speed
#endif
#ifndef BLUR_OPTIMIZED_CLAMP
#define BLUR_OPTIMIZED_CLAMP 0 // [Experimental] Testing a new optimized clamp ISA
#endif
// constants
#define BLUR_TILE_SIZE_Y_INV (1.0 / FFX_BLUR_TILE_SIZE_Y)
//--------------------------------------------------------------------------------------
// GROUPSHARED MEMORY
//--------------------------------------------------------------------------------------
// Define CacheTypes<FP16, SOA>
#if BLUR_GROUPSHARED_MEMORY_SOA
#if BLUR_GROUPSHARED_MEMORY_HALF
#ifdef FFX_HLSL
#define BLUR_GROUPSHARED_MEMORY_TYPE groupshared FfxFloat16
#else
#define BLUR_GROUPSHARED_MEMORY_TYPE shared FfxFloat16
#endif
#else
#ifdef FFX_HLSL
#define BLUR_GROUPSHARED_MEMORY_TYPE groupshared FfxFloat32
#else
#define BLUR_GROUPSHARED_MEMORY_TYPE shared FfxFloat32
#endif
#endif
#else // BLUR_GROUPSHARED_MEMORY_SOA
#if BLUR_GROUPSHARED_MEMORY_HALF
#ifdef FFX_HLSL
#define BLUR_GROUPSHARED_MEMORY_TYPE groupshared FfxFloat16x3
#else
#define BLUR_GROUPSHARED_MEMORY_TYPE shared FfxFloat16x3
#endif
#else
#ifdef FFX_HLSL
#define BLUR_GROUPSHARED_MEMORY_TYPE groupshared FfxFloat32x3
#else
#define BLUR_GROUPSHARED_MEMORY_TYPE shared FfxFloat32x3
#endif
#endif
#endif // BLUR_GROUPSHARED_MEMORY_SOA
//==============================================================================================================================
// MATH HELPERS
//==============================================================================================================================
#define DIV_AND_ROUND_UP(x, y) (((x) + ((y)-1)) / ((y)))
// Fast modulo operator for powers of two values for Y: x % y == x & (y-1)
#define FAST_MOD(x, y) ((x) & (y - 1))
#if FFX_HALF
#define FAST_MOD16(x, y) ((x) & (y - FfxInt16(1)))
#endif
// OUTPUT CACHE ########################################
/*
# Notes from Jordan's Presentation
src: https://gpuopen.com/gdc-presentations/2019/gdc-2019-s5-blend-of-gcn-optimization-and-color-processing.pdf
Use 2^n tiles to use bitwise AND in place of the more ALU-expensive % operator, see #define FAST_MOD above
MinTiles -> Ceil(HalfKernel / TileSize) * 2 + 1
*/
#if FFX_BLUR_OPTION_KERNEL_DIMENSION > 7
#define NUM_TILES_OUTPUT_CACHE 8
#else
#define NUM_TILES_OUTPUT_CACHE 4
#endif
#define NUM_PIXELS_OUTPUT_CACHE (FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_TILE_SIZE_X * NUM_TILES_OUTPUT_CACHE)
#if BLUR_GROUPSHARED_MEMORY_PK_UINT
#ifdef FFX_HLSL
groupshared FfxUInt32 OutputCacheRG[NUM_PIXELS_OUTPUT_CACHE]; // RG: 2x fp16's are packed into 32bit unsigned int
groupshared FfxFloat32 OutputCacheB [NUM_PIXELS_OUTPUT_CACHE]; // B : don't use fp16 for B to avoid bank conflicts
#else
shared FfxUInt32 OutputCacheRG[NUM_PIXELS_OUTPUT_CACHE]; // RG: 2x fp16's are packed into 32bit unsigned int
shared FfxFloat32 OutputCacheB [NUM_PIXELS_OUTPUT_CACHE]; // B : don't use fp16 for B to avoid bank conflicts
#endif
#else
#if BLUR_GROUPSHARED_MEMORY_SOA
BLUR_GROUPSHARED_MEMORY_TYPE OutputCache[NUM_PIXELS_OUTPUT_CACHE * 3]; // stores rrrrrr...ggggggg...bbbbb...
#else
BLUR_GROUPSHARED_MEMORY_TYPE OutputCache[NUM_PIXELS_OUTPUT_CACHE]; // stores rgbrgbrgbrgbrgbrgbrgbrgb...
#endif // BLUR_GROUPSHARED_MEMORY_SOA
#endif // BLUR_GROUPSHARED_MEMORY_PK_UINT
// Based on the FFX_BLUR_OPTION_KERNEL_DIMENSION, we will need to pre-fill a number of tiles.
// e.g. TILE_SIZE_Y=8
// -----------------------------------------
// kernel = 3 | NUM_PREFILL_TILES_OUTPUT_CACHE = 1 |
// kernel = 5 | NUM_PREFILL_TILES_OUTPUT_CACHE = 1 |
// kernel = 7 | NUM_PREFILL_TILES_OUTPUT_CACHE = 1 |
// kernel = 9* | NUM_PREFILL_TILES_OUTPUT_CACHE = 2*|
// kernel = 11 | NUM_PREFILL_TILES_OUTPUT_CACHE = 2 |
// kernel = 13 | NUM_PREFILL_TILES_OUTPUT_CACHE = 2 |
// kernel = 15 | NUM_PREFILL_TILES_OUTPUT_CACHE = 2 |
// kernel = 17*| NUM_PREFILL_TILES_OUTPUT_CACHE = 3*|
// kernel = 19 | NUM_PREFILL_TILES_OUTPUT_CACHE = 3 |
// kernel = 21 | NUM_PREFILL_TILES_OUTPUT_CACHE = 3 |
// kernel = 23 | NUM_PREFILL_TILES_OUTPUT_CACHE = 3 |
// -----------------------------------------
#define NUM_PREFILL_TILES_OUTPUT_CACHE DIV_AND_ROUND_UP(FFX_BLUR_OPTION_KERNEL_DIMENSION, FFX_BLUR_TILE_SIZE_Y)
// INPUT CACHE ########################################
#if BLUR_ENABLE_INPUT_CACHE
#define INPUT_CACHE_TILE_SIZE_X (FFX_BLUR_TILE_SIZE_X + FFX_BLUR_OPTION_KERNEL_DIMENSION - 1)
#define NUM_TILES_INPUT_CACHE 1
#define NUM_PIXELS_INPUT_CACHE ((INPUT_CACHE_TILE_SIZE_X * FFX_BLUR_TILE_SIZE_Y) * NUM_TILES_INPUT_CACHE)
#if BLUR_GROUPSHARED_MEMORY_PK_UINT
#ifdef FFX_HLSL
groupshared FfxUInt32 InputCacheRG[NUM_PIXELS_INPUT_CACHE]; // RG: 2x fp16's are packed into 32bit unsigned int
groupshared FfxFloat32 InputCacheB [NUM_PIXELS_INPUT_CACHE]; // B : don't use fp16 for B to avoid bank conflicts
#else
shared FfxUInt32 InputCacheRG[NUM_PIXELS_INPUT_CACHE]; // RG: 2x fp16's are packed into 32bit unsigned int
shared FfxFloat32 InputCacheB [NUM_PIXELS_INPUT_CACHE]; // B : don't use fp16 for B to avoid bank conflicts
#endif
#else
#if BLUR_GROUPSHARED_MEMORY_SOA
BLUR_GROUPSHARED_MEMORY_TYPE InputCache[NUM_PIXELS_INPUT_CACHE * 3]; // stores rrrrrr...ggggggg...bbbbb...
#else
BLUR_GROUPSHARED_MEMORY_TYPE InputCache[NUM_PIXELS_INPUT_CACHE]; // stores rgbrgbrgbrgbrgbrgbrgbrgb...
#endif // BLUR_GROUPSHARED_MEMORY_SOA
#endif //BLUR_GROUPSHARED_MEMORY_PK_UINT
#endif // BLUR_ENABLE_INPUT_CACHE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//==============================================================================================================================
// GROUPSHARED MEMORY MAPPING FUNCTIONS
//==============================================================================================================================
// LDS ops ----------------------------------------------------------------------------------
// LDS TILES : FFX_BLUR_TILE_SIZE_X * FFX_BLUR_TILE_SIZE_Y sized tiles
// e.g. FFX_BLUR_TILE_SIZE_X = 8
//
// <------------ FFX_BLUR_TILE_SIZE_X -------------->
// ^ OutputCache[0-7]
// | OutputCache[8-15]
// |
// | // TILE #1
// |
// |
// v OutputCache[56-63]
// <------------------------------------------------>
// |
// <------------ FFX_BLUR_TILE_SIZE_X -------------->
// ^ OutputCache[64-71]
// |
// |
// | // TILE #2
// |
// |
// v
// <------------------------------------------------>
// |
//...
FfxUInt32 PackF2(FfxFloat32x2 c) { return (ffxF32ToF16(c.r) << 16) | ffxF32ToF16(c.g); }
FfxFloat32x2 UnpackToF2(FfxUInt32 packedRG)
{
#ifdef FFX_HLSL
return f16tof32(FfxUInt32x2(packedRG >> 16, packedRG & 0xFFFF));
#else
return unpackHalf2x16(packedRG).yx;
#endif
}
#if FFX_HALF
FfxUInt32 PackH2(FfxFloat16x2 c) { return (ffxF32ToF16(FfxFloat32(c.r)) << 16) | ffxF32ToF16(FfxFloat32(c.g)); } // TODO: is there a cast fp16->FfxUInt32 and skip fp16->fp32 promotion?
FfxFloat16x2 UnpackToH2(FfxUInt32 packedRG){ return FfxFloat16x2(UnpackToF2(packedRG)); }
#endif
#ifdef FFX_HLSL
inline FfxUInt32 FlattenIndex(FfxInt32x2 Index, FfxInt32 ElementStride)
#else
FfxUInt32 FlattenIndex(FfxInt32x2 Index, FfxInt32 ElementStride)
#endif
{
return Index.x + Index.y * ElementStride;
}
#if BLUR_GROUPSHARED_MEMORY_SOA
void SetOutputCache(FfxInt32x2 index, FfxFloat32x3 value)
{
FfxInt32 iLDS = index.x + index.y * BLUR_TILE_SIZE_X;
OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 0] = value.r;
OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 1] = value.g;
OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 2] = value.b;
}
FfxFloat32x3 GetOutputCache(FfxInt32x2 index)
{
FfxInt32 iLDS = index.x + index.y * FFX_BLUR_TILE_SIZE_X;
FfxFloat32x3 c;
c.r = OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 0];
c.g = OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 1];
c.b = OutputCache[iLDS + NUM_PIXELS_OUTPUT_CACHE * 2];
return c;
}
#if BLUR_ENABLE_INPUT_CACHE
void SetInputCache(FfxInt32x2 index, FfxFloat32x3 value)
{
FfxInt32 iLDS = index.x + index.y * INPUT_CACHE_TILE_SIZE_X;
InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 0] = value.r;
InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 1] = value.g;
InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 2] = value.b;
}
FfxFloat32x3 GetInputCache(FfxInt32x2 index)
{
FfxInt32 iLDS = index.x + index.y * INPUT_CACHE_TILE_SIZE_X;
FfxFloat32x3 c;
c.r = InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 0];
c.g = InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 1];
c.b = InputCache[iLDS + NUM_PIXELS_INPUT_CACHE * 2];
return c;
}
#endif // BLUR_ENABLE_INPUT_CACHE
#else
#if BLUR_GROUPSHARED_MEMORY_PK_UINT
#if FFX_HALF
void SetOutputCache(FfxInt32x2 index, FfxFloat16x3 value)
{
FfxInt32 iLDS = index.x + index.y * FFX_BLUR_TILE_SIZE_X;
OutputCacheRG[iLDS] = PackH2(value.rg);
OutputCacheB[iLDS] = value.b;
}
FfxFloat16x3 GetOutputCache(FfxInt32x2 index)
{
FfxInt32 iLDS = index.x + index.y * FFX_BLUR_TILE_SIZE_X;
FfxFloat16x2 RG = UnpackToH2(OutputCacheRG[iLDS]);
return FfxFloat16x3(RG.r, RG.g, OutputCacheB[iLDS]);
}
#else
void SetOutputCache(FfxInt32x2 index, FfxFloat32x3 value)
{
FfxInt32 iLDS = index.x + index.y * FFX_BLUR_TILE_SIZE_X;
OutputCacheRG[iLDS] = PackF2(value.rg);
OutputCacheB[iLDS] = value.b;
}
FfxFloat32x3 GetOutputCache(FfxInt32x2 index)
{
FfxInt32 iLDS = index.x + index.y * FFX_BLUR_TILE_SIZE_X;
FfxFloat32x2 RG = UnpackToF2(OutputCacheRG[iLDS]);
return FfxFloat32x3(RG.r, RG.g, OutputCacheB[iLDS]);
}
#endif // FFX_HALF
#else
#if FFX_HALF
void SetOutputCache(FfxInt32x2 index, FfxFloat16x3 value)
{
const FfxUInt32 iLDS = FlattenIndex(index, FFX_BLUR_TILE_SIZE_X);
OutputCache[iLDS] = value;
}
FfxFloat16x3 GetOutputCache(FfxInt32x2 index)
{
const FfxUInt32 iLDS = FlattenIndex(index, FFX_BLUR_TILE_SIZE_X);
return OutputCache[iLDS];
}
#else
void SetOutputCache(FfxInt32x2 index, FfxFloat32x3 value)
{
const FfxUInt32 iLDS = FlattenIndex(index, FFX_BLUR_TILE_SIZE_X);
OutputCache[iLDS] = value;
}
FfxFloat32x3 GetOutputCache(FfxInt32x2 index)
{
const FfxUInt32 iLDS = FlattenIndex(index, FFX_BLUR_TILE_SIZE_X);
return OutputCache[iLDS];
}
#endif // FFX_HALF
#endif // BLUR_GROUPSHARED_MEMORY_PK_UINT
#if BLUR_ENABLE_INPUT_CACHE
#if BLUR_GROUPSHARED_MEMORY_PK_UINT
#if FFX_HALF
void SetInputCache(FfxInt32x2 index, FfxFloat16x3 value)
{
FfxInt32 iLDS = FlattenIndex(index, INPUT_CACHE_TILE_SIZE_X);
InputCacheRG[iLDS] = PackH2(value.rg);
InputCacheB[iLDS] = value.b;
}
FfxFloat16x3 GetInputCache(FfxInt32x2 index)
{
FfxInt32 iLDS = FlattenIndex(index, INPUT_CACHE_TILE_SIZE_X);
FfxFloat16x2 RG = UnpackToH2(InputCacheRG[iLDS]);
return FfxFloat16x3(RG.r, RG.g, InputCacheB[iLDS]);
}
#else
void SetInputCache(FfxInt32x2 index, FfxFloat32x3 value)
{
//FfxInt32 iLDS = FlattenIndex(index, INPUT_CACHE_TILE_SIZE_X);
FfxInt32 iLDS = index.x + index.y * INPUT_CACHE_TILE_SIZE_X;
InputCacheRG[iLDS] = PackF2(value.rg);
InputCacheB[iLDS] = value.b;
}
FfxFloat32x3 GetInputCache(FfxInt32x2 index)
{
//FfxInt32 iLDS = FlattenIndex(index, INPUT_CACHE_TILE_SIZE_X);
FfxInt32 iLDS = index.x + index.y * INPUT_CACHE_TILE_SIZE_X;
FfxFloat32x2 RG = UnpackToF2(InputCacheRG[iLDS]);
return FfxFloat32x3(RG.r, RG.g, InputCacheB[iLDS]);
}
#endif // FFX_HALF
#else
void SetInputCache(FfxInt32x2 index, FfxFloat32x3 value) { InputCache[index.x + index.y * INPUT_CACHE_TILE_SIZE_X].rgb = value; }
FfxFloat32x3 GetInputCache(FfxInt32x2 index) { return InputCache[index.x + index.y * INPUT_CACHE_TILE_SIZE_X].rgb; }
#endif // BLUR_GROUPSHARED_MEMORY_PK_UINT
#endif // BLUR_ENABLE_INPUT_CACHE
#endif // BLUR_GROUPSHARED_MEMORY_SOA
void LDSBarrier()
{
FFX_GROUP_MEMORY_BARRIER;
}
// index of the LDS tile in the ring buffer
#if FFX_HALF
#if BLUR_FP16_KERNEL_LOOPS
FfxInt16 GetOutputCacheTile(FfxInt16 iTile) { return FAST_MOD16(iTile, FfxInt16(NUM_TILES_OUTPUT_CACHE)); }
void CacheInOutputTile(FfxInt32x2 threadID, FfxInt16 iTile, FfxFloat16x3 color)
{
FfxInt16 iLDSTile = GetOutputCacheTile(iTile);
FfxInt16x2 TileOffset = FfxInt16x2(0, FfxInt16(FFX_BLUR_TILE_SIZE_Y) * iLDSTile);
FfxInt16x2 iLDS = FfxInt16x2(threadID) + TileOffset;
SetOutputCache(iLDS, color);
}
FfxFloat16x3 LoadFromCachedOutputTile(FfxInt32x2 threadID, FfxInt16 iTile)
{
FfxInt16 iLDSTile = GetOutputCacheTile(iTile);
FfxInt16x2 TileOffset = FfxInt16x2(0, FfxInt16(FFX_BLUR_TILE_SIZE_Y) * iLDSTile);
FfxInt16x2 iLDS = FfxInt16x2(threadID) + TileOffset;
return GetOutputCache(iLDS);
}
#else // BLUR_FP16_KERNEL_LOOPS
FfxInt32 GetOutputCacheTile(FfxInt32 iTile) { return FAST_MOD(iTile, NUM_TILES_OUTPUT_CACHE); }
void CacheInOutputTile(FfxInt32x2 threadID, FfxInt32 iTile, FfxFloat16x3 color)
{
FfxInt32 iLDSTile = GetOutputCacheTile(iTile);
FfxInt32x2 TileOffset = FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iLDSTile);
FfxInt32x2 iLDS = threadID + TileOffset;
SetOutputCache(iLDS, color);
}
FfxFloat32x3 LoadFromCachedOutputTile(FfxInt32x2 threadID, FfxInt32 iTile)
{
FfxInt32 iLDSTile = GetOutputCacheTile(iTile);
FfxInt32x2 TileOffset = FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iLDSTile);
FfxInt32x2 iLDS = threadID + TileOffset;
return GetOutputCache(iLDS);
}
#endif // BLUR_FP16_KERNEL_LOOPS
#else
FfxInt32 GetOutputCacheTile(FfxInt32 iTile) { return FAST_MOD(iTile, NUM_TILES_OUTPUT_CACHE); }
void CacheInOutputTile(FfxInt32x2 threadID, FfxInt32 iTile, FfxFloat32x3 color)
{
FfxInt32 iLDSTile = GetOutputCacheTile(iTile); // map image tile to LDS ring buffered tiles
FfxInt32x2 TileOffset = FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iLDSTile); // pixel offset for this tile
FfxInt32x2 iLDS = threadID + TileOffset; // 2D LDS coord based on local thread ID
SetOutputCache(iLDS, color);
}
FfxFloat32x3 LoadFromCachedOutputTile(FfxInt32x2 threadID, FfxInt32 iTile)
{
FfxInt32 iLDSTile = GetOutputCacheTile(iTile); // map image tile to LDS ring buffered tiles
FfxInt32x2 TileOffset = FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iLDSTile); // pixel offset for this tile
FfxInt32x2 iLDS = threadID + TileOffset; // 2D LDS coord based on local thread ID
return GetOutputCache(iLDS);
}
#endif // FFX_HALF
#if BLUR_ENABLE_INPUT_CACHE
#if FFX_HALF
void CacheInInputTile(FfxInt32x2 threadID, FfxInt32 KernelOffset, FfxFloat16x3 c)
#else
void CacheInInputTile(FfxInt32x2 threadID, FfxInt32 KernelOffset, FfxFloat32x3 c)
#endif
{
FfxInt32x2 InputCacheCoord = threadID + FfxInt32x2(KernelOffset, 0) + FfxInt32x2(FFX_BLUR_KERNEL_RANGE-1, 0);
SetInputCache(InputCacheCoord, c);
}
#if FFX_HALF
FfxFloat16x3 LoadFromCachedInputTile(FfxInt32x2 threadID, FfxInt32 KernelOffset)
#else
FfxFloat32x3 LoadFromCachedInputTile(FfxInt32x2 threadID, FfxInt32 KernelOffset)
#endif
{
FfxInt32x2 InputCacheCoord = threadID + FfxInt32x2(KernelOffset,0) + FfxInt32x2(FFX_BLUR_KERNEL_RANGE-1, 0);
return GetInputCache(InputCacheCoord);
}
#endif // BLUR_ENABLE_INPUT_CACHE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//==============================================================================================================================
// BLUR FUNCTIONS
//==============================================================================================================================
#if FFX_HALF
FfxFloat16x3 HorizontalBlurFromTexture(FfxInt32x2 CenterPixelLocation, FfxInt32x2 ImageSize)
{
#if BLUR_FP16_CLAMP // this uses 4 less VGPRs but not faster
const FfxInt16 ImageSizeClampValueX = FfxInt16(ImageSize.x - 1);
FfxFloat16x3 BlurredImage = BlurLoadInput(FfxInt16x2(CenterPixelLocation)) * BlurLoadKernelWeight(0);
for (FfxInt32 i = 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt32x2 Offset = FfxInt32x2(i, 0);
FfxInt16x2 SampleCoordXX = FfxInt16x2(CenterPixelLocation.x + i, CenterPixelLocation.x - i);
#if !BLUR_DISABLE_CLAMP
SampleCoordXX = clamp(SampleCoordXX, FfxInt16x2(0, 0), FfxInt16x2(ImageSizeClampValueX, ImageSizeClampValueX));
#endif
BlurredImage += BlurLoadInput(FfxInt16x2(SampleCoordXX[0], CenterPixelLocation.y)) * BlurLoadKernelWeight(i);
BlurredImage += BlurLoadInput(FfxInt16x2(SampleCoordXX[1], CenterPixelLocation.y)) * BlurLoadKernelWeight(i);
}
#else
FfxFloat16x3 BlurredImage = FfxFloat16x3(0.f, 0.f, 0.f);
for (FfxInt32 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt32x2 SampleCoord = CenterPixelLocation + FfxInt32x2(i, 0); // horizontal blur
#if !BLUR_DISABLE_CLAMP
SampleCoord.x = clamp(SampleCoord.x, 0, ImageSize.x-1); // clamp
#endif
FfxFloat16x3 c = BlurLoadInput(SampleCoord);
BlurredImage += c * BlurLoadKernelWeight(abs(i));
}
#endif // BLUR_FP16_CLAMP
return BlurredImage;
}
#else // FFX_HALF
FfxFloat32x3 HorizontalBlurFromTexture(FfxInt32x2 CenterPixelLocation, FfxInt32x2 ImageSize)
{
FfxFloat32x3 BlurredImage = FfxFloat32x3(0.f, 0.f, 0.f);
for (FfxInt32 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt32x2 SampleCoord = CenterPixelLocation + FfxInt32x2(i, 0); // horizontal blur
#if !BLUR_DISABLE_CLAMP
SampleCoord.x = clamp(SampleCoord.x, 0, ImageSize.x-1); // clamp
#endif
FfxFloat32x3 c = BlurLoadInput(SampleCoord);
BlurredImage += c * BlurLoadKernelWeight(abs(i));
}
return BlurredImage;
}
#endif // FFX_HALF
#if BLUR_ENABLE_INPUT_CACHE
#if FFX_HALF
FfxFloat16x3 HorizontalBlurFromCachedInput(FfxInt32x2 threadID)
#else
FfxFloat32x3 HorizontalBlurFromCachedInput(FfxInt32x2 threadID)
#endif
{
#if FFX_HALF
FfxFloat16x3 BlurredImage = FfxFloat16x3(0.f, 0.f, 0.f);
#else
FfxFloat32x3 BlurredImage = FfxFloat32x3(0.f, 0.f, 0.f);
#endif // FFX_HALF
for (FfxInt32 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
BlurredImage += LoadFromCachedInputTile(threadID, i) * BlurLoadKernelWeight(abs(i));
}
return BlurredImage;
}
#endif // BLUR_ENABLE_INPUT_CACHE
#if FFX_HALF
#if BLUR_FP16_KERNEL_LOOPS
FfxFloat16x3 VerticalBlurFromCachedOutput(FfxInt32x2 ThreadID, FfxInt32x2 WorkGroupID,FfxInt16x2 CenterPixelLocation, FfxInt16x2 ImageSize)
{
const FfxInt16x2 ImageSizeClampValueXY = ImageSize.xy - FfxInt16x2(1, 1);
const FfxUInt32 iTileCount = DIV_AND_ROUND_UP(FfxUInt32(ImageSize.y), FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_DISPATCH_Y);
FfxFloat16x3 value = FfxFloat16x3(0, 0, 0);
#ifndef FFX_HLSL
// For some reason using 16 bit integer for this loop in glsl does not work. It seems to be due to the use of
// a negative value as a starting value that is compared to a positive value, which seems to incorrectly cause
// the condition to always be false.
for (FfxInt32 i = (FfxInt32(-FFX_BLUR_KERNEL_RANGE) + FfxInt32(1)); i < FfxInt32(FFX_BLUR_KERNEL_RANGE); ++i)
#else
for (FfxInt16 i = (FfxInt16(-FFX_BLUR_KERNEL_RANGE) + FfxInt16(1)); i < FfxInt16(FFX_BLUR_KERNEL_RANGE); ++i)
#endif
{
FfxInt16x2 KernelSampleLocation = CenterPixelLocation + FfxInt16x2(0, i);
#if !BLUR_DISABLE_CLAMP
KernelSampleLocation.xy = clamp(KernelSampleLocation.xy, FfxInt16x2(0, 0), ImageSizeClampValueXY);
#endif
const FfxInt16 iTile_ImageSpace = FfxInt16(KernelSampleLocation.y * BLUR_TILE_SIZE_Y_INV);
const FfxInt16 iTile = FfxInt16(iTile_ImageSpace - iTileCount * WorkGroupID.y);
FfxInt16x2 TileThreadID = FfxInt16x2(ThreadID.x, FAST_MOD16(KernelSampleLocation.y, FfxInt16(FFX_BLUR_TILE_SIZE_Y)));
FfxFloat16x3 c = LoadFromCachedOutputTile(TileThreadID, iTile);
value += c * BlurLoadKernelWeight(abs(i));
}
return value;
}
#else
FfxFloat16x3 VerticalBlurFromCachedOutput(FfxInt32x2 ThreadID, FfxInt32x2 WorkGroupID, FfxInt32x2 CenterPixelLocation, FfxInt32x2 ImageSize)
{
const FfxUInt32 iTileCount = DIV_AND_ROUND_UP(ImageSize.y, FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_DISPATCH_Y);
FfxFloat16x3 value = FfxFloat16x3(0, 0, 0);
#if BLUR_FP16_CLAMP
const FfxInt16x2 ClampUpperLimitXY = ImageSize.xy - 1;
for (FfxInt16 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt16x2 KernelSampleLocation = CenterPixelLocation + FfxInt16x2(0, i);
#if !BLUR_DISABLE_CLAMP
#if BLUR_OPTIMIZED_CLAMP
bool bNegative = firstbithigh(KernelSampleLocation.x) == 31;
KernelSampleLocation.y = bNegative ? 0 : KernelSampleLocation.y;
#else
KernelSampleLocation.xy = clamp(KernelSampleLocation.xy, FfxInt16x2(0, 0), ClampUpperLimitXY);
#endif // BLUR_OPTIMIZED_CLAMP
#endif
const FfxInt16 iTile_ImageSpace = FfxInt16(KernelSampleLocation.y * FFX_BLUR_TILE_SIZE_Y_INV);
const FfxInt16 iTile = iTile_ImageSpace - iTileCount * WorkGroupID.y;
FfxInt16x2 TileThreadID = FfxInt16x2(ThreadID.x, FAST_MOD16(KernelSampleLocation.y, FfxInt16(FFX_BLUR_TILE_SIZE_Y)));
FfxFloat16x3 c = LoadFromCachedOutputTile(TileThreadID, iTile);
value += c * BlurLoadKernelWeight(abs(i));
}
#else
for (FfxInt32 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt32x2 KernelSampleLocation = CenterPixelLocation + FfxInt32x2(0, i);
#if !BLUR_DISABLE_CLAMP
#if BLUR_OPTIMIZED_CLAMP
bool bNegative = firstbithigh(KernelSampleLocation.x) == 31;
KernelSampleLocation.y = bNegative ? 0 : KernelSampleLocation.y;
#else
KernelSampleLocation.xy = clamp(KernelSampleLocation.xy, 0, ImageSize.xy-1);
#endif // BLUR_OPTIMIZED_CLAMP
#endif
const FfxInt32 iTile_ImageSpace = FfxInt32(KernelSampleLocation.y * FFX_BLUR_TILE_SIZE_Y_INV);
const FfxInt32 iTile = iTile_ImageSpace - (iTileCountPerWorkgroup * WorkGroupID.y);
FfxInt32x2 TileThreadID = FfxInt32x2(ThreadID.x, FAST_MOD(KernelSampleLocation.y, FFX_BLUR_TILE_SIZE_Y));
FfxFloat16x3 c = LoadFromCachedOutputTile(TileThreadID, iTile);
value += c * BlurLoadKernelWeight(abs(i));
}
#endif // BLUR_FP16_CLAMP
return value;
}
#endif // BLUR_FP16_KERNEL_LOOPS
#else // FFX_HALF
FfxFloat32x3 VerticalBlurFromCachedOutput(FfxInt32x2 ThreadID, FfxInt32x2 WorkGroupID,FfxInt32x2 CenterPixelLocation, FfxInt32x2 ImageSize)
{
const FfxInt32 iTileCountPerWorkgroup = DIV_AND_ROUND_UP(ImageSize.y, FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_DISPATCH_Y);
FfxFloat32x3 value = FfxFloat32x3(0,0,0);
for (FfxInt32 i = -FFX_BLUR_KERNEL_RANGE + 1; i < FFX_BLUR_KERNEL_RANGE; ++i)
{
FfxInt32x2 KernelSampleLocation = CenterPixelLocation + FfxInt32x2(0, i);
#if !BLUR_DISABLE_CLAMP
KernelSampleLocation.xy = clamp(KernelSampleLocation.xy, FfxInt32x2(0, 0), ImageSize.xy-1);
#endif
// which 'global' tile in the image space
const FfxInt32 iTile_ImageSpace = FfxInt32(KernelSampleLocation.y * BLUR_TILE_SIZE_Y_INV);
// local tile in this workgroup - apply the offset to convert to local space tile coordinates
// this is needed for workgroups that have WorkgroupID.y > 0: the previous workgroup's
// tile mapping doesn't have to align with the current one's depending on the FFX_BLUR_TILE_SIZE_XY.
// e.g. WorkGroupID=1's first tile will map to 0 in local space, but could be some non-0 index
// in the local space of the previous workgroup (WorkGroupID=0).
// Not correcting for this mapping will result in a chopped image on the workgroup borders.
const FfxInt32 iTile = iTile_ImageSpace - (iTileCountPerWorkgroup * WorkGroupID.y);
FfxInt32x2 TileThreadID = FfxInt32x2(ThreadID.x, FAST_MOD(KernelSampleLocation.y, FFX_BLUR_TILE_SIZE_Y));
//FfxInt32x2 TileThreadID = FfxInt32x2(ThreadID.x, KernelSampleLocation.y % BLUR_TILE_SIZE_Y);
value += LoadFromCachedOutputTile(TileThreadID, iTile) * BlurLoadKernelWeight(abs(i));
}
return value;
}
#endif // FFX_HALF
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//==============================================================================================================================
// INPUT/OUTPUT CACHE HELPERS
//==============================================================================================================================
//
#if BLUR_ENABLE_INPUT_CACHE
// Fills the input cache with the corresponding rgion from the image + kernel extents
void FillInputCache(in FfxInt32x2 lxy, FfxInt32x2 CenterPixelLocation, FfxInt32x2 ImageSize)
{
#if FFX_HALF
FfxFloat16x3 c = FfxFloat16x3(0, 0, 0);
#else
FfxFloat32x3 c = FfxFloat32x3(0, 0, 0);
#endif
// slide the thread group over the InputCache
const FfxInt32 iNumLoops = DIV_AND_ROUND_UP(INPUT_CACHE_TILE_SIZE_X, FFX_BLUR_TILE_SIZE_X);
for (FfxInt32 i = 0; i < iNumLoops; ++i)
{
FfxInt32x2 LDSCoord = lxy - FfxInt32x2(FFX_BLUR_KERNEL_RANGE - 1, 0) + FfxInt32x2(i * FFX_BLUR_TILE_SIZE_X, 0);
FfxInt32x2 SamplePosition = CenterPixelLocation - FfxInt32x2(FFX_BLUR_KERNEL_RANGE - 1, 0) + FfxInt32x2(i * FFX_BLUR_TILE_SIZE_X, 0);
#if !BLUR_DISABLE_CLAMP
SamplePosition.x = clamp(SamplePosition.x, 0, ImageSize.x - 1);
#endif
c = BlurLoadInput(SamplePosition);
// clamp to LDS bounds if we're on the last iteration of the loop
if (i == iNumLoops - 1)
{
// faster than 'if (LDSCoord.x < INPUT_CACHE_TILE_SIZE_X)', avoids a vmem sync at the cost of some ALU
LDSCoord.x = clamp(LDSCoord.x, -FfxInt32x2(FFX_BLUR_KERNEL_RANGE - 1, 0), (INPUT_CACHE_TILE_SIZE_X - 1) - FfxInt32x2(FFX_BLUR_KERNEL_RANGE - 1, 0));
}
CacheInInputTile(LDSCoord, 0, c);
}
LDSBarrier();
}
#endif // BLUR_ENABLE_INPUT_CACHE
// Fills the output cache with the horizontally-blurred image.
void PreFillOutputCache(in FfxInt32x2 gxy, in FfxInt32x2 lxy, in FfxInt32x2 WorkGroupID, FfxInt32x2 ImageSize)
{
#if BLUR_DEBUG_PREFILL_OUTPUT_CACHE_WITH_COLOR
FfxFloat32x3 FillColor = FfxFloat32x3(0, 0, 0); // black border color
[unroll]
for (FfxInt32 iTile = 0; iTile < NUM_TILES_OUTPUT_CACHE; ++iTile)
{
CacheInOutputTile(lxy, iTile, FillColor);
}
LDSBarrier();
#endif
// load from VMEM the first NUM_PREFILL_TILES_OUTPUT_CACHE tiles
// while doing the horizontal blur, going top down
#if FFX_HALF
for (FfxInt16 j = FfxInt16(0); j < FfxInt16(NUM_PREFILL_TILES_OUTPUT_CACHE); ++j)
#else
for (FfxInt32 j = 0; j < NUM_PREFILL_TILES_OUTPUT_CACHE; ++j)
#endif
{
const FfxInt32x2 ImageCoordinate = gxy + FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * j);
#if FFX_HALF
FfxFloat16x3 c = HorizontalBlurFromTexture(ImageCoordinate, ImageSize);
#else
FfxFloat32x3 c = HorizontalBlurFromTexture(ImageCoordinate, ImageSize);
#endif
CacheInOutputTile(lxy, j, c);
}
#if FFX_BLUR_DISPATCH_Y != 1
// for any workgroup that doesn't start frop the top of the image,
// fill the cache from the tail, going upwards in the image space
if (WorkGroupID.y != 0)
{
#if FFX_HALF
FfxFloat16x3 c = FfxFloat16x3(0, 0, 0);
#else
FfxFloat32x3 c = FfxFloat32x3(0, 0, 0);
#endif
#if FFX_HALF
for (FfxInt16 j = FfxInt16(1); j < FfxInt16(NUM_PREFILL_TILES_OUTPUT_CACHE+1 + (DIV_AND_ROUND_UP(FFX_BLUR_KERNEL_RANGE, FFX_BLUR_TILE_SIZE_Y))); ++j)
#else
for (FfxInt32 j = 1; j < NUM_PREFILL_TILES_OUTPUT_CACHE+1 + (DIV_AND_ROUND_UP(FFX_BLUR_KERNEL_RANGE, FFX_BLUR_TILE_SIZE_Y)); ++j)
#endif
{
const FfxInt32x2 ImageCoordinate = gxy - FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * j);
#if BLUR_ENABLE_INPUT_CACHE
FillInputCache(lxy, ImageCoordinate, ImageSize);
c = HorizontalBlurFromCachedInput(lxy);
#else
c = HorizontalBlurFromTexture(ImageCoordinate, ImageSize);
#endif
#if FFX_HALF
CacheInOutputTile(lxy, (FfxInt16(NUM_TILES_OUTPUT_CACHE) - j), c);
#else
CacheInOutputTile(lxy, (NUM_TILES_OUTPUT_CACHE - j), c);
#endif
}
}
#endif
LDSBarrier(); // OutputCache Sync: Read -> Write =========================================
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//==============================================================================================================================
// BLUR GAUSSIAN BLUR ALGORITHM
//==============================================================================================================================
/// ffxBlur: The main idea of the algorithm is to utilize a number of tiles (8x8) that are cached on the groupshared memory
/// in a ring-buffer fashion to speed up texture lookups in a hand-optimized compute shader.
/// The tiles are defined by the FFX_BLUR_TILE_SIZE_X and FFX_BLUR_TILE_SIZE_Y defines, and are typically 8x8 pixels.
/// The image is horizontally blurred while being cached on the groupshared memory,
/// and when all the groupshared tiles are filled, a vertical blur pass is done on the groupshared memory
/// and the result is stored in the UAV as the final destination.
///
/// The algorithm is as follows:
/// - Pre-fill LDS with 8x8 tiles, storing vertical tiles, containing horizontally blurred color
/// - Loop until the entire image is covered:
/// - Run a vertical blur pass on the LDS and output to final destination UAV
/// - Re-fill LDS with horizontally-blurred data
/// - Finish off the remaining last row/section of the image
///
/// @param [in] GlobalThreadID The SV_DispatchThreadID.xy or gl_GlobalInvocationID.xy.
/// @param [in] WorkGroupLocalThreadID The SV_GroupThreadID.xy or gl_LocalInvocationID.xy.
/// @param [in] WorkGroupID The SV_GroupID.xy or gl_WorkGroupID.xy.
/// @param [in] ImageSize The two dimensional size of the input and output image.
///
/// @ingroup FfxGPUBlur
void ffxBlur(
in FfxInt32x2 GlobalThreadID,
in FfxInt32x2 WorkGroupLocalThreadID,
in FfxInt32x2 WorkGroupID,
FfxInt32x2 ImageSize)
{
// Each threadgroup processes a number of tiles of size FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_TILE_SIZE_X
// This number depends on the image height and the vertical dimension (_Y) of the FFX_BLUR_TILE_SIZE
//const FfxUInt32 iTileCount = DIV_AND_ROUND_UP(ImageSize.y, FFX_BLUR_TILE_SIZE_Y);
const FfxUInt32 iTileCount =
DIV_AND_ROUND_UP(
ImageSize.y,
FFX_BLUR_TILE_SIZE_Y * FFX_BLUR_DISPATCH_Y);
FfxInt32x2 gxy = FfxInt32x2(
WorkGroupID.x * FFX_BLUR_TILE_SIZE_X + WorkGroupLocalThreadID.x
, WorkGroupLocalThreadID.y + WorkGroupID.y * iTileCount * FFX_BLUR_TILE_SIZE_Y
);
FfxInt32x2 lxy = WorkGroupLocalThreadID;
if (gxy.x >= ImageSize.x)
return;
//-------------------------------------------------------------------------------------------------
// STEP #1
//-------------------------------------------------------------------------------------------------
// Pre-fill the output cache with a few tiles of horizontally blurred image.
// The tile count to pre-fill is a function of kernel width and TileSizeY.
PreFillOutputCache(gxy, lxy, WorkGroupID, ImageSize); // doesn't sync waves
//-------------------------------------------------------------------------------------------------
// STEP #2
//-------------------------------------------------------------------------------------------------
// loop through the tiles and write out to UAV as we go from top to down
FfxInt32 iTileOutput = 0;
#if FFX_HALF
FfxFloat16x3 c = FfxFloat16x3(0, 0, 0);
#else
FfxFloat32x3 c = FfxFloat32x3(0, 0, 0);
#endif
for (; iTileOutput < iTileCount - NUM_PREFILL_TILES_OUTPUT_CACHE; ++iTileOutput)
{
// index of next tile that we'll cache the output to
// It runs ahead of the tile we will be writing out to UAV by NUM_PREFILL_TILES_OUTPUT_CACHE tiles
FfxInt32 iNextTileOutputCache = iTileOutput + NUM_PREFILL_TILES_OUTPUT_CACHE;
const FfxInt32x2 HorizontalBlurInputCoord = gxy + FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iNextTileOutputCache);
// run horizontal blur & cache the next output tile
#if BLUR_ENABLE_INPUT_CACHE
FillInputCache(lxy, HorizontalBlurInputCoord, ImageSize);
c = HorizontalBlurFromCachedInput(lxy);
#else
// Number of image_load instructions will scale with FFX_BLUR_OPTION_KERNEL_DIMENSION.
c = HorizontalBlurFromTexture(HorizontalBlurInputCoord, ImageSize);
#endif // BLUR_ENABLE_INPUT_CACHE
#if FFX_HALF
CacheInOutputTile(lxy, FfxInt16(iNextTileOutputCache), c);
#else
CacheInOutputTile(lxy, iNextTileOutputCache, c);
#endif
LDSBarrier(); // OutputCache Sync: Write -> Read =========================================
// Start writing out the pixel value which has its final value
// convolved from the pixels aready in the LDS section.
const FfxInt32x2 OutputCoord = gxy + FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iTileOutput);
#if FFX_HALF
c = VerticalBlurFromCachedOutput(lxy, WorkGroupID, FfxInt16x2(OutputCoord), FfxInt16x2(ImageSize));
#else
c = VerticalBlurFromCachedOutput(lxy, WorkGroupID, OutputCoord, ImageSize);
#endif
BlurStoreOutput(OutputCoord, c);
LDSBarrier(); // OutputCache Sync: Read -> Write =========================================
}
//-------------------------------------------------------------------------------------------------
// STEP #3
//-------------------------------------------------------------------------------------------------
// fill in the remaining last tiles (= loop for NUM_PREFILL_TILES_OUTPUT_CACHE)
for (; iTileOutput < iTileCount; ++iTileOutput)
{
const FfxInt32x2 OutputCoord = gxy + FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iTileOutput);
if (iTileOutput >= iTileCount - NUM_PREFILL_TILES_OUTPUT_CACHE)
{
FfxInt32 iNextTileOutputCache = iTileOutput + NUM_PREFILL_TILES_OUTPUT_CACHE;
const FfxInt32x2 HorizontalBlurInputCoord = gxy + FfxInt32x2(0, FFX_BLUR_TILE_SIZE_Y * iNextTileOutputCache);
// run horizontal blur & cache the next output tile
#if BLUR_ENABLE_INPUT_CACHE
FillInputCache(lxy, HorizontalBlurInputCoord, ImageSize);
c = HorizontalBlurFromCachedInput(lxy);
#else
// Number of image_load instructions will scale with FFX_BLUR_OPTION_KERNEL_DIMENSION.
c = HorizontalBlurFromTexture(HorizontalBlurInputCoord, ImageSize);
#endif // BLUR_ENABLE_INPUT_CACHE
#if FFX_HALF
CacheInOutputTile(lxy, FfxInt16(iNextTileOutputCache), c);
#else
CacheInOutputTile(lxy, iNextTileOutputCache, c);
#endif
LDSBarrier(); // OutputCache Sync: Write -> Read =========================================
}
#if FFX_HALF
c = VerticalBlurFromCachedOutput(lxy, WorkGroupID, FfxInt16x2(OutputCoord), FfxInt16x2(ImageSize));
#else
c = VerticalBlurFromCachedOutput(lxy, WorkGroupID, OutputCoord, ImageSize);
#endif
BlurStoreOutput(OutputCoord, c);
}
}
#endif // !FFX_CPU
#endif // FFX_BLUR_H

View File

@@ -0,0 +1,65 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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 "ffx_core.h"
FFX_BLUR_KERNEL_TYPE BlurLoadKernelWeight(FfxInt32 iKernelIndex)
{
return FfxBlurLoadKernelWeight(iKernelIndex);
}
#if FFX_HALF
void BlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat16x3 color)
{
FfxBlurStoreOutput(outPxCoord, color);
}
FfxFloat16x3 BlurLoadInput(FfxInt16x2 inPxCoord)
{
return FfxBlurLoadInput(inPxCoord);
}
#else // FFX_HALF
void BlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color)
{
FfxBlurStoreOutput(outPxCoord, color);
}
// DXIL generates load/sync/store blocks for each channel, ticket open: https://ontrack-internal.amd.com/browse/SWDEV-303837
// this is 10x times slower!!!
//void Blur_StoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color) { texColorOutput[outPxCoord].rgb = color; }
FfxFloat32x3 BlurLoadInput(FfxInt32x2 inPxCoord)
{
return FfxBlurLoadInput(inPxCoord);
}
#endif // !FFX_HALF
#include "blur/ffx_blur.h"
void ffxBlurPass(FfxInt32x2 DispatchThreadID, FfxInt32x2 LocalThreadId, FfxInt32x2 WorkGroupId)
{
ffxBlur(DispatchThreadID, LocalThreadId, WorkGroupId, ImageSize());
}

View File

@@ -0,0 +1,216 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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 "ffx_blur_resources.h"
#if defined(FFX_GPU)
#include "ffx_core.h"
#ifndef FFX_PREFER_WAVE64
#define FFX_PREFER_WAVE64
#endif // #if defined(FFX_PREFER_WAVE64)
#pragma warning(disable: 3205) // conversion from larger type to smaller
#if defined(FFX_BLUR_BIND_CB_BLUR)
layout (set = 0, binding = FFX_BLUR_BIND_CB_BLUR, std140) uniform cbBLUR_t
{
FfxInt32x2 imageSize;
} cbBLUR;
#endif
FfxInt32x2 ImageSize()
{
return cbBLUR.imageSize;
}
// SRVs
#if defined FFX_BLUR_BIND_SRV_INPUT_SRC
layout (set = 0, binding = FFX_BLUR_BIND_SRV_INPUT_SRC) uniform texture2D r_input_src;
#endif
// UAV declarations
#if defined FFX_BLUR_BIND_UAV_OUTPUT
layout (set = 0, binding = FFX_BLUR_BIND_UAV_OUTPUT, rgba32f) uniform image2D rw_output;
#endif
// FFX_BLUR_OPTION_KERNEL_DIMENSION to be defined by the client application
// App should define e.g the following for 5x5 blur:
// #define FFX_BLUR_OPTION_KERNEL_DIMENSION 5
#ifndef FFX_BLUR_OPTION_KERNEL_DIMENSION
#error Please define FFX_BLUR_OPTION_KERNEL_DIMENSION
#endif
// FFX_BLUR_KERNEL_RANGE is center + half width of the kernel
//
// consider a blur kernel 5x5 - '*' indicates the center of the kernel
// FFX_BLUR_OPTION_KERNEL_DIMENSION=5
// |---------------|
// x x x x x
// x x x x x
// x x x* x x
// x x x x x
// x x x x x
//
//
// as separate 1D kernels
//
// x x x* x x
// |-------|
// FFX_BLUR_KERNEL_RANGE
//
#define FFX_BLUR_KERNEL_RANGE (((FFX_BLUR_OPTION_KERNEL_DIMENSION - 1) / 2) + 1)
#define FFX_BLUR_KERNEL_RANGE_MINUS1 (FFX_BLUR_KERNEL_RANGE - 1)
//
// FFX-Blur Callback definitions
//
#if FFX_HALF
#define FFX_BLUR_KERNEL_TYPE FfxFloat16
#else
#define FFX_BLUR_KERNEL_TYPE FfxFloat32
#endif
FFX_BLUR_KERNEL_TYPE FfxBlurLoadKernelWeight(FfxInt32 iKernelIndex)
{
// GAUSSIAN BLUR 1D KERNELS
//
//----------------------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------------------
// Kernel Size: [3, 21]: odd numbers
// Kernels are pregenerated using three different sigma values.
// Larger sigmas are better for larger kernels.
const FFX_BLUR_KERNEL_TYPE kernel_weights[FFX_BLUR_KERNEL_RANGE] =
#if FFX_BLUR_OPTION_KERNEL_PERMUTATION == 0
// Sigma: 1.6
#if FFX_BLUR_KERNEL_RANGE == 2
{ FFX_BLUR_KERNEL_TYPE(0.3765770884), FFX_BLUR_KERNEL_TYPE(0.3117114558) };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ FFX_BLUR_KERNEL_TYPE(0.2782163289), FFX_BLUR_KERNEL_TYPE(0.230293397), FFX_BLUR_KERNEL_TYPE(0.1305984385) };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ FFX_BLUR_KERNEL_TYPE(0.2525903052), FFX_BLUR_KERNEL_TYPE(0.2090814714), FFX_BLUR_KERNEL_TYPE(0.1185692428), FFX_BLUR_KERNEL_TYPE(0.0460541333) };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ FFX_BLUR_KERNEL_TYPE(0.2465514351), FFX_BLUR_KERNEL_TYPE(0.2040828004), FFX_BLUR_KERNEL_TYPE(0.115734517), FFX_BLUR_KERNEL_TYPE(0.0449530818), FFX_BLUR_KERNEL_TYPE(0.0119538834) };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ FFX_BLUR_KERNEL_TYPE(0.245483563), FFX_BLUR_KERNEL_TYPE(0.2031988699), FFX_BLUR_KERNEL_TYPE(0.1152332436), FFX_BLUR_KERNEL_TYPE(0.0447583794), FFX_BLUR_KERNEL_TYPE(0.0119021083), FFX_BLUR_KERNEL_TYPE(0.0021656173) };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ FFX_BLUR_KERNEL_TYPE(0.2453513488), FFX_BLUR_KERNEL_TYPE(0.2030894296), FFX_BLUR_KERNEL_TYPE(0.1151711805), FFX_BLUR_KERNEL_TYPE(0.0447342732), FFX_BLUR_KERNEL_TYPE(0.011895698), FFX_BLUR_KERNEL_TYPE(0.0021644509), FFX_BLUR_KERNEL_TYPE(0.0002692935) };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ FFX_BLUR_KERNEL_TYPE(0.2453401155), FFX_BLUR_KERNEL_TYPE(0.2030801313), FFX_BLUR_KERNEL_TYPE(0.1151659074), FFX_BLUR_KERNEL_TYPE(0.044732225), FFX_BLUR_KERNEL_TYPE(0.0118951533), FFX_BLUR_KERNEL_TYPE(0.0021643518), FFX_BLUR_KERNEL_TYPE(0.0002692811), FFX_BLUR_KERNEL_TYPE(2.28922E-05) };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ FFX_BLUR_KERNEL_TYPE(0.2453394635), FFX_BLUR_KERNEL_TYPE(0.2030795916), FFX_BLUR_KERNEL_TYPE(0.1151656014), FFX_BLUR_KERNEL_TYPE(0.0447321061), FFX_BLUR_KERNEL_TYPE(0.0118951217), FFX_BLUR_KERNEL_TYPE(0.0021643461), FFX_BLUR_KERNEL_TYPE(0.0002692804), FFX_BLUR_KERNEL_TYPE(2.28922E-05), FFX_BLUR_KERNEL_TYPE(1.3287E-06) };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ FFX_BLUR_KERNEL_TYPE(0.2453394377), FFX_BLUR_KERNEL_TYPE(0.2030795703), FFX_BLUR_KERNEL_TYPE(0.1151655892), FFX_BLUR_KERNEL_TYPE(0.0447321014), FFX_BLUR_KERNEL_TYPE(0.0118951205), FFX_BLUR_KERNEL_TYPE(0.0021643458), FFX_BLUR_KERNEL_TYPE(0.0002692804), FFX_BLUR_KERNEL_TYPE(2.28922E-05), FFX_BLUR_KERNEL_TYPE(1.3287E-06), FFX_BLUR_KERNEL_TYPE(5.26E-08) };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ FFX_BLUR_KERNEL_TYPE(0.2453394371), FFX_BLUR_KERNEL_TYPE(0.2030795697), FFX_BLUR_KERNEL_TYPE(0.1151655889), FFX_BLUR_KERNEL_TYPE(0.0447321013), FFX_BLUR_KERNEL_TYPE(0.0118951204), FFX_BLUR_KERNEL_TYPE(0.0021643458), FFX_BLUR_KERNEL_TYPE(0.0002692804), FFX_BLUR_KERNEL_TYPE(2.28922E-05), FFX_BLUR_KERNEL_TYPE(1.3287E-06), FFX_BLUR_KERNEL_TYPE(5.26E-08), FFX_BLUR_KERNEL_TYPE(1.4E-09) };
#endif
#elif FFX_BLUR_OPTION_KERNEL_PERMUTATION == 1
// Sigma: 2.8
#if FFX_BLUR_KERNEL_RANGE == 2
{ FFX_BLUR_KERNEL_TYPE(0.3474999743), FFX_BLUR_KERNEL_TYPE(0.3262500129) };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ FFX_BLUR_KERNEL_TYPE(0.2256541468), FFX_BLUR_KERNEL_TYPE(0.2118551763), FFX_BLUR_KERNEL_TYPE(0.1753177504) };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ FFX_BLUR_KERNEL_TYPE(0.1796953063), FFX_BLUR_KERNEL_TYPE(0.1687067636), FFX_BLUR_KERNEL_TYPE(0.1396108926), FFX_BLUR_KERNEL_TYPE(0.1018346906) };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ FFX_BLUR_KERNEL_TYPE(0.1588894947), FFX_BLUR_KERNEL_TYPE(0.1491732476), FFX_BLUR_KERNEL_TYPE(0.1234462081), FFX_BLUR_KERNEL_TYPE(0.0900438796), FFX_BLUR_KERNEL_TYPE(0.0578919173) };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ FFX_BLUR_KERNEL_TYPE(0.1491060676), FFX_BLUR_KERNEL_TYPE(0.1399880866), FFX_BLUR_KERNEL_TYPE(0.1158451582), FFX_BLUR_KERNEL_TYPE(0.0844995374), FFX_BLUR_KERNEL_TYPE(0.054327293), FFX_BLUR_KERNEL_TYPE(0.0307868909) };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ FFX_BLUR_KERNEL_TYPE(0.1446570603), FFX_BLUR_KERNEL_TYPE(0.1358111404), FFX_BLUR_KERNEL_TYPE(0.1123885856), FFX_BLUR_KERNEL_TYPE(0.0819782513), FFX_BLUR_KERNEL_TYPE(0.0527062824), FFX_BLUR_KERNEL_TYPE(0.0298682757), FFX_BLUR_KERNEL_TYPE(0.0149189344) };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ FFX_BLUR_KERNEL_TYPE(0.1427814521), FFX_BLUR_KERNEL_TYPE(0.1340502275), FFX_BLUR_KERNEL_TYPE(0.110931367), FFX_BLUR_KERNEL_TYPE(0.0809153299), FFX_BLUR_KERNEL_TYPE(0.0520228983), FFX_BLUR_KERNEL_TYPE(0.0294810068), FFX_BLUR_KERNEL_TYPE(0.0147254971), FFX_BLUR_KERNEL_TYPE(0.0064829474) };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ FFX_BLUR_KERNEL_TYPE(0.1420666821), FFX_BLUR_KERNEL_TYPE(0.1333791663), FFX_BLUR_KERNEL_TYPE(0.1103760399), FFX_BLUR_KERNEL_TYPE(0.0805102644), FFX_BLUR_KERNEL_TYPE(0.0517624694), FFX_BLUR_KERNEL_TYPE(0.0293334236), FFX_BLUR_KERNEL_TYPE(0.0146517806), FFX_BLUR_KERNEL_TYPE(0.0064504935), FFX_BLUR_KERNEL_TYPE(0.0025030212) };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ FFX_BLUR_KERNEL_TYPE(0.1418238658), FFX_BLUR_KERNEL_TYPE(0.1331511984), FFX_BLUR_KERNEL_TYPE(0.1101873883), FFX_BLUR_KERNEL_TYPE(0.0803726585), FFX_BLUR_KERNEL_TYPE(0.0516739985), FFX_BLUR_KERNEL_TYPE(0.0292832877), FFX_BLUR_KERNEL_TYPE(0.0146267382), FFX_BLUR_KERNEL_TYPE(0.0064394685), FFX_BLUR_KERNEL_TYPE(0.0024987432), FFX_BLUR_KERNEL_TYPE(0.0008545858) };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ FFX_BLUR_KERNEL_TYPE(0.1417508359), FFX_BLUR_KERNEL_TYPE(0.1330826344), FFX_BLUR_KERNEL_TYPE(0.1101306491), FFX_BLUR_KERNEL_TYPE(0.0803312719), FFX_BLUR_KERNEL_TYPE(0.0516473898), FFX_BLUR_KERNEL_TYPE(0.0292682088), FFX_BLUR_KERNEL_TYPE(0.0146192064), FFX_BLUR_KERNEL_TYPE(0.0064361526), FFX_BLUR_KERNEL_TYPE(0.0024974565), FFX_BLUR_KERNEL_TYPE(0.0008541457), FFX_BLUR_KERNEL_TYPE(0.0002574667) };
#endif
#elif FFX_BLUR_OPTION_KERNEL_PERMUTATION == 2
// Sigma: 4
#if FFX_BLUR_KERNEL_RANGE == 2
{ FFX_BLUR_KERNEL_TYPE(0.3402771036), FFX_BLUR_KERNEL_TYPE(0.3298614482) };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ FFX_BLUR_KERNEL_TYPE(0.2125433723), FFX_BLUR_KERNEL_TYPE(0.2060375614), FFX_BLUR_KERNEL_TYPE(0.1876907525) };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ FFX_BLUR_KERNEL_TYPE(0.1608542243), FFX_BLUR_KERNEL_TYPE(0.1559305837), FFX_BLUR_KERNEL_TYPE(0.1420455978), FFX_BLUR_KERNEL_TYPE(0.1215967064) };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ FFX_BLUR_KERNEL_TYPE(0.1345347233), FFX_BLUR_KERNEL_TYPE(0.1304167051), FFX_BLUR_KERNEL_TYPE(0.1188036266), FFX_BLUR_KERNEL_TYPE(0.1017006505), FFX_BLUR_KERNEL_TYPE(0.0818116562) };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ FFX_BLUR_KERNEL_TYPE(0.1197258568), FFX_BLUR_KERNEL_TYPE(0.1160611281), FFX_BLUR_KERNEL_TYPE(0.1057263555), FFX_BLUR_KERNEL_TYPE(0.090505984), FFX_BLUR_KERNEL_TYPE(0.0728062644), FFX_BLUR_KERNEL_TYPE(0.0550373395) };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ FFX_BLUR_KERNEL_TYPE(0.1110429695), FFX_BLUR_KERNEL_TYPE(0.1076440182), FFX_BLUR_KERNEL_TYPE(0.0980587551), FFX_BLUR_KERNEL_TYPE(0.0839422118), FFX_BLUR_KERNEL_TYPE(0.0675261302), FFX_BLUR_KERNEL_TYPE(0.0510458624), FFX_BLUR_KERNEL_TYPE(0.0362615375) };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ FFX_BLUR_KERNEL_TYPE(0.1059153311), FFX_BLUR_KERNEL_TYPE(0.1026733334), FFX_BLUR_KERNEL_TYPE(0.0935306896), FFX_BLUR_KERNEL_TYPE(0.0800660068), FFX_BLUR_KERNEL_TYPE(0.0644079717), FFX_BLUR_KERNEL_TYPE(0.0486887143), FFX_BLUR_KERNEL_TYPE(0.0345870861), FFX_BLUR_KERNEL_TYPE(0.0230885324) };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ FFX_BLUR_KERNEL_TYPE(0.1029336421), FFX_BLUR_KERNEL_TYPE(0.0997829119), FFX_BLUR_KERNEL_TYPE(0.0908976484), FFX_BLUR_KERNEL_TYPE(0.0778120183), FFX_BLUR_KERNEL_TYPE(0.0625947824), FFX_BLUR_KERNEL_TYPE(0.0473180477), FFX_BLUR_KERNEL_TYPE(0.0336134033), FFX_BLUR_KERNEL_TYPE(0.0224385526), FFX_BLUR_KERNEL_TYPE(0.0140758142) };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ FFX_BLUR_KERNEL_TYPE(0.1012533395), FFX_BLUR_KERNEL_TYPE(0.0981540422), FFX_BLUR_KERNEL_TYPE(0.089413823), FFX_BLUR_KERNEL_TYPE(0.0765418045), FFX_BLUR_KERNEL_TYPE(0.0615729768), FFX_BLUR_KERNEL_TYPE(0.0465456216), FFX_BLUR_KERNEL_TYPE(0.0330646936), FFX_BLUR_KERNEL_TYPE(0.0220722627), FFX_BLUR_KERNEL_TYPE(0.0138460388), FFX_BLUR_KERNEL_TYPE(0.0081620671) };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ FFX_BLUR_KERNEL_TYPE(0.1003459368), FFX_BLUR_KERNEL_TYPE(0.0972744146), FFX_BLUR_KERNEL_TYPE(0.0886125226), FFX_BLUR_KERNEL_TYPE(0.0758558594), FFX_BLUR_KERNEL_TYPE(0.0610211779), FFX_BLUR_KERNEL_TYPE(0.0461284934), FFX_BLUR_KERNEL_TYPE(0.0327683775), FFX_BLUR_KERNEL_TYPE(0.0218744576), FFX_BLUR_KERNEL_TYPE(0.0137219546), FFX_BLUR_KERNEL_TYPE(0.008088921), FFX_BLUR_KERNEL_TYPE(0.0044808529) };
#endif
#else
#error FFX_BLUR_OPTION_KERNEL_PERMUTATION is not a valid value.
#endif // FFX_BLUR_OPTION_KERNEL_PERMUTATIONs
return kernel_weights[iKernelIndex];
}
#if FFX_HALF
#if defined (FFX_BLUR_BIND_UAV_OUTPUT)
void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat16x3 color)
{
imageStore(rw_output, outPxCoord, FfxFloat16x4(color, 1));
}
#endif // #if defined (FFX_BLUR_BIND_UAV_OUTPUT)
#if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
FfxFloat16x3 FfxBlurLoadInput(FfxInt16x2 inPxCoord)
{
return FfxFloat16x3(texelFetch(r_input_src, inPxCoord, 0).rgb);
}
#endif // #if defined FFX_BLUR_BIND_SRV_INPUT_SRC
#else // FFX_HALF
#if defined (FFX_BLUR_BIND_UAV_OUTPUT)
void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color)
{
imageStore(rw_output, outPxCoord, FfxFloat32x4(color, 1));
}
#endif // #if defined FFX_BLUR_BIND_UAV_OUTPUT
// DXIL generates load/sync/store blocks for each channel, ticket open: https://ontrack-internal.amd.com/browse/SWDEV-303837
// this is 10x times slower!!!
//void Blur_StoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color) { texColorOutput[outPxCoord].rgb = color; }
#if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
FfxFloat32x3 FfxBlurLoadInput(FfxInt32x2 inPxCoord)
{
return texelFetch(r_input_src, inPxCoord, 0).rgb;
}
#endif // #if defined FFX_BLUR_BIND_SRV_INPUT_SRC
#endif // !FFX_HALF
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,254 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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 "ffx_blur_resources.h"
#if defined(FFX_GPU)
#ifdef __hlsl_dx_compiler
#pragma dxc diagnostic push
#pragma dxc diagnostic ignored "-Wambig-lit-shift"
#endif //__hlsl_dx_compiler
#include "ffx_core.h"
#ifdef __hlsl_dx_compiler
#pragma dxc diagnostic pop
#endif //__hlsl_dx_compiler
#ifndef FFX_PREFER_WAVE64
#define FFX_PREFER_WAVE64
#endif // #ifndef FFX_PREFER_WAVE64
#if defined(FFX_GPU)
#pragma warning(disable: 3205) // conversion from larger type to smaller
#endif // #if defined(FFX_GPU)
#define DECLARE_SRV_REGISTER(regIndex) t##regIndex
#define DECLARE_UAV_REGISTER(regIndex) u##regIndex
#define DECLARE_CB_REGISTER(regIndex) b##regIndex
#define FFX_BLUR_DECLARE_SRV(regIndex) register(DECLARE_SRV_REGISTER(regIndex))
#define FFX_BLUR_DECLARE_UAV(regIndex) register(DECLARE_UAV_REGISTER(regIndex))
#define FFX_BLUR_DECLARE_CB(regIndex) register(DECLARE_CB_REGISTER(regIndex))
#if defined(FFX_BLUR_BIND_CB_BLUR)
cbuffer cbBLUR : FFX_BLUR_DECLARE_CB(FFX_BLUR_BIND_CB_BLUR)
{
FfxInt32x2 imageSize;
#define FFX_BLUR_CONSTANT_BUFFER_1_SIZE 2 // Number of 32-bit values. This must be kept in sync with the cbBLUR size.
};
#else
#define imageSize 0
#endif
#define FFX_BLUR_ROOTSIG_STRINGIFY(p) FFX_BLUR_ROOTSIG_STR(p)
#define FFX_BLUR_ROOTSIG_STR(p) #p
#define FFX_BLUR_ROOTSIG [RootSignature("DescriptorTable(UAV(u0, numDescriptors = " FFX_BLUR_ROOTSIG_STRINGIFY(FFX_BLUR_RESOURCE_IDENTIFIER_COUNT) ")), " \
"DescriptorTable(SRV(t0, numDescriptors = " FFX_BLUR_ROOTSIG_STRINGIFY(FFX_BLUR_RESOURCE_IDENTIFIER_COUNT) ")), " \
"CBV(b0)" )]
#if defined(FFX_BLUR_EMBED_ROOTSIG)
#define FFX_BLUR_EMBED_ROOTSIG_CONTENT FFX_BLUR_ROOTSIG
#else
#define FFX_BLUR_EMBED_ROOTSIG_CONTENT
#endif // #if FFX_BLUR_EMBED_ROOTSIG
FfxInt32x2 ImageSize()
{
return imageSize;
}
// SRVs
#if defined FFX_BLUR_BIND_SRV_INPUT_SRC
#if FFX_HALF
Texture2D<FfxFloat16x4> r_input_src : FFX_BLUR_DECLARE_SRV(FFX_BLUR_BIND_SRV_INPUT_SRC);
#else
Texture2D<FfxFloat32x4> r_input_src : FFX_BLUR_DECLARE_SRV(FFX_BLUR_BIND_SRV_INPUT_SRC);
#endif // FFX_HALF
#endif
// UAV declarations
#if defined FFX_BLUR_BIND_UAV_OUTPUT
#if FFX_HALF
RWTexture2D<FfxFloat16x4> rw_output : FFX_BLUR_DECLARE_UAV(FFX_BLUR_BIND_UAV_OUTPUT);
#else
RWTexture2D<FfxFloat32x4> rw_output : FFX_BLUR_DECLARE_UAV(FFX_BLUR_BIND_UAV_OUTPUT);
#endif // FFX_HALF
#endif
// FFX_BLUR_OPTION_KERNEL_DIMENSION to be defined by the client application
// App should define e.g the following for 5x5 blur:
// #define FFX_BLUR_OPTION_KERNEL_DIMENSION 5
#ifndef FFX_BLUR_OPTION_KERNEL_DIMENSION
#error Please define FFX_BLUR_OPTION_KERNEL_DIMENSION
#endif
// FFX_BLUR_KERNEL_RANGE is center + half width of the kernel
//
// consider a blur kernel 5x5 - '*' indicates the center of the kernel
// FFX_BLUR_OPTION_KERNEL_DIMENSION=5
// |---------------|
// x x x x x
// x x x x x
// x x x* x x
// x x x x x
// x x x x x
//
//
// as separate 1D kernels
//
// x x x* x x
// |-------|
// FFX_BLUR_KERNEL_RANGE
//
#define FFX_BLUR_KERNEL_RANGE (((FFX_BLUR_OPTION_KERNEL_DIMENSION - 1) / 2) + 1)
#define FFX_BLUR_KERNEL_RANGE_MINUS1 (FFX_BLUR_KERNEL_RANGE - 1)
//
// FFX-Blur Callback definitions
//
#if FFX_HALF
#define FFX_BLUR_KERNEL_TYPE FfxFloat16
#else
#define FFX_BLUR_KERNEL_TYPE FfxFloat32
#endif
inline FFX_BLUR_KERNEL_TYPE FfxBlurLoadKernelWeight(FfxInt32 iKernelIndex)
{
// GAUSSIAN BLUR 1D KERNELS
//
//----------------------------------------------------------------------------------------------------------------------------------
// Kernel Size: [3, 21]: odd numbers
// Kernels are pregenerated using three different sigma values.
// Larger sigmas are better for larger kernels.
const FFX_BLUR_KERNEL_TYPE kernel_weights[FFX_BLUR_KERNEL_RANGE] =
#if FFX_BLUR_OPTION_KERNEL_PERMUTATION == 0
// Sigma: 1.6
#if FFX_BLUR_KERNEL_RANGE == 2
{ 0.3765770884, 0.3117114558 };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ 0.2782163289, 0.230293397, 0.1305984385 };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ 0.2525903052, 0.2090814714, 0.1185692428, 0.0460541333 };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ 0.2465514351, 0.2040828004, 0.115734517, 0.0449530818, 0.0119538834 };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ 0.245483563, 0.2031988699, 0.1152332436, 0.0447583794, 0.0119021083, 0.0021656173 };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ 0.2453513488, 0.2030894296, 0.1151711805, 0.0447342732, 0.011895698, 0.0021644509, 0.0002692935 };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ 0.2453401155, 0.2030801313, 0.1151659074, 0.044732225, 0.0118951533, 0.0021643518, 0.0002692811, 2.28922E-05 };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ 0.2453394635, 0.2030795916, 0.1151656014, 0.0447321061, 0.0118951217, 0.0021643461, 0.0002692804, 2.28922E-05, 1.3287E-06 };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ 0.2453394377, 0.2030795703, 0.1151655892, 0.0447321014, 0.0118951205, 0.0021643458, 0.0002692804, 2.28922E-05, 1.3287E-06, 5.26E-08 };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ 0.2453394371, 0.2030795697, 0.1151655889, 0.0447321013, 0.0118951204, 0.0021643458, 0.0002692804, 2.28922E-05, 1.3287E-06, 5.26E-08, 1.4E-09 };
#endif
#elif FFX_BLUR_OPTION_KERNEL_PERMUTATION == 1
// Sigma: 2.8
#if FFX_BLUR_KERNEL_RANGE == 2
{ 0.3474999743, 0.3262500129 };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ 0.2256541468, 0.2118551763, 0.1753177504 };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ 0.1796953063, 0.1687067636, 0.1396108926, 0.1018346906 };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ 0.1588894947, 0.1491732476, 0.1234462081, 0.0900438796, 0.0578919173 };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ 0.1491060676, 0.1399880866, 0.1158451582, 0.0844995374, 0.054327293, 0.0307868909 };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ 0.1446570603, 0.1358111404, 0.1123885856, 0.0819782513, 0.0527062824, 0.0298682757, 0.0149189344 };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ 0.1427814521, 0.1340502275, 0.110931367, 0.0809153299, 0.0520228983, 0.0294810068, 0.0147254971, 0.0064829474 };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ 0.1420666821, 0.1333791663, 0.1103760399, 0.0805102644, 0.0517624694, 0.0293334236, 0.0146517806, 0.0064504935, 0.0025030212 };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ 0.1418238658, 0.1331511984, 0.1101873883, 0.0803726585, 0.0516739985, 0.0292832877, 0.0146267382, 0.0064394685, 0.0024987432, 0.0008545858 };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ 0.1417508359, 0.1330826344, 0.1101306491, 0.0803312719, 0.0516473898, 0.0292682088, 0.0146192064, 0.0064361526, 0.0024974565, 0.0008541457, 0.0002574667 };
#endif
#elif FFX_BLUR_OPTION_KERNEL_PERMUTATION == 2
// Sigma: 4
#if FFX_BLUR_KERNEL_RANGE == 2
{ 0.3402771036, 0.3298614482 };
#elif FFX_BLUR_KERNEL_RANGE == 3
{ 0.2125433723, 0.2060375614, 0.1876907525 };
#elif FFX_BLUR_KERNEL_RANGE == 4
{ 0.1608542243, 0.1559305837, 0.1420455978, 0.1215967064 };
#elif FFX_BLUR_KERNEL_RANGE == 5
{ 0.1345347233, 0.1304167051, 0.1188036266, 0.1017006505, 0.0818116562 };
#elif FFX_BLUR_KERNEL_RANGE == 6
{ 0.1197258568, 0.1160611281, 0.1057263555, 0.090505984, 0.0728062644, 0.0550373395 };
#elif FFX_BLUR_KERNEL_RANGE == 7
{ 0.1110429695, 0.1076440182, 0.0980587551, 0.0839422118, 0.0675261302, 0.0510458624, 0.0362615375 };
#elif FFX_BLUR_KERNEL_RANGE == 8
{ 0.1059153311, 0.1026733334, 0.0935306896, 0.0800660068, 0.0644079717, 0.0486887143, 0.0345870861, 0.0230885324 };
#elif FFX_BLUR_KERNEL_RANGE == 9
{ 0.1029336421, 0.0997829119, 0.0908976484, 0.0778120183, 0.0625947824, 0.0473180477, 0.0336134033, 0.0224385526, 0.0140758142 };
#elif FFX_BLUR_KERNEL_RANGE == 10
{ 0.1012533395, 0.0981540422, 0.089413823, 0.0765418045, 0.0615729768, 0.0465456216, 0.0330646936, 0.0220722627, 0.0138460388, 0.0081620671 };
#elif FFX_BLUR_KERNEL_RANGE == 11
{ 0.1003459368, 0.0972744146, 0.0886125226, 0.0758558594, 0.0610211779, 0.0461284934, 0.0327683775, 0.0218744576, 0.0137219546, 0.008088921, 0.0044808529 };
#endif
#else
#error FFX_BLUR_OPTION_KERNEL_PERMUTATION is not a valid value.
#endif // FFX_BLUR_OPTION_KERNEL_PERMUTATIONs
return kernel_weights[iKernelIndex];
}
#if FFX_HALF
#if defined (FFX_BLUR_BIND_UAV_OUTPUT)
void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat16x3 color)
{
rw_output[outPxCoord] = FfxFloat16x4(color, 1);
}
#endif // #if defined (FFX_BLUR_BIND_UAV_OUTPUT)
#if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
FfxFloat16x3 FfxBlurLoadInput(FfxInt16x2 inPxCoord)
{
return r_input_src[inPxCoord].rgb;
}
#endif // #if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
#else // FFX_HALF
#if defined (FFX_BLUR_BIND_UAV_OUTPUT)
void FfxBlurStoreOutput(FfxInt32x2 outPxCoord, FfxFloat32x3 color)
{
rw_output[outPxCoord] = FfxFloat32x4(color, 1);
}
#endif // #if defined (FFX_BLUR_BIND_UAV_OUTPUT)
#if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
FfxFloat32x3 FfxBlurLoadInput(FfxInt32x2 inPxCoord)
{
return r_input_src[inPxCoord].rgb;
}
#endif // #if defined (FFX_BLUR_BIND_SRV_INPUT_SRC)
#endif // !FFX_HALF
#endif // #if defined(FFX_GPU)

View File

@@ -0,0 +1,39 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BLUR_RESOURCES_H
#define FFX_BLUR_RESOURCES_H
#if defined(FFX_CPU) || defined(FFX_GPU)
#define FFX_BLUR_RESOURCE_IDENTIFIER_NULL 0
#define FFX_BLUR_RESOURCE_IDENTIFIER_INPUT_SRC 1
#define FFX_BLUR_RESOURCE_IDENTIFIER_OUTPUT 2
#define FFX_BLUR_RESOURCE_IDENTIFIER_COUNT 3
// CBV resource definitions
#define FFX_BLUR_CONSTANTBUFFER_IDENTIFIER_BLUR 0
#endif // #if defined(FFX_CPU) || defined(FFX_GPU)
#endif // FFX_BLUR_RESOURCES_H

View File

@@ -0,0 +1,74 @@
# This file is part of the FidelityFX SDK.
#
# Copyright (C) 2024 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and /or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
set(BRIXELIZER_BASE_ARGS
-reflection -deps=gcc -DFFX_GPU=1)
set(BRIXELIZER_INCLUDE_ARGS
"${FFX_GPU_PATH}"
"${FFX_GPU_PATH}/brixelizer")
if (NOT BRIXELIZER_SHADER_EXT)
set(BRIXELIZER_SHADER_EXT *)
endif()
file(GLOB BRIXELIZER_SHADERS
"shaders/brixelizer/ffx_brixelizer_cascade_ops_build_tree_aabb_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_clear_brick_storage_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_clear_build_counters_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_clear_job_counter_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_clear_ref_counters_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_coarse_culling_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_compact_references_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_compress_brick_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_emit_sdf_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_free_cascade_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_initialize_cascade_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_invalidate_job_areas_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_mark_cascade_uninitialized_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_reset_cascade_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_scan_jobs_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_scan_references_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_scroll_cascade_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_cascade_ops_voxelize_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_clear_brick_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_clear_counters_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_collect_clear_bricks_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_collect_dirty_bricks_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_eikonal_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_merge_bricks_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_merge_cascades_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_prepare_clear_bricks_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_prepare_eikonal_args_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_context_ops_prepare_merge_bricks_args_pass.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_debug_draw_aabb_tree.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_debug_draw_instance_aabbs.${BRIXELIZER_SHADER_EXT}"
"shaders/brixelizer/ffx_brixelizer_debug_visualization_pass.${BRIXELIZER_SHADER_EXT}")
# compile all the shaders
compile_shaders_with_depfile(
"${FFX_SC_EXECUTABLE}"
"${BRIXELIZER_BASE_ARGS}" "${BRIXELIZER_API_BASE_ARGS}" "" "${BRIXELIZER_INCLUDE_ARGS}"
"${BRIXELIZER_SHADERS}" "${FFX_PASS_SHADER_OUTPUT_PATH}" BRIXELIZER_PERMUTATION_OUTPUTS)
# add the header files they generate to the main list of dependencies
add_shader_output("${BRIXELIZER_PERMUTATION_OUTPUTS}")

View File

@@ -0,0 +1,117 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_BRICK_COMMON_H
#define FFX_BRIXELIZER_BRICK_COMMON_H
#include "ffx_brixelizer_common.h"
FfxUInt32 FfxBrixelizerBrickGetIndex(FfxUInt32 brick_id)
{
return brick_id & FFX_BRIXELIZER_BRICK_ID_MASK;
}
FfxUInt32x3 FfxBrixelizerWrapCoords(FfxInt32x3 clipmap_offset, FfxUInt32 wrap_mask, FfxUInt32x3 voxel_coord)
{
return (voxel_coord + FfxUInt32x3(clipmap_offset)) & FfxUInt32x3(wrap_mask, wrap_mask, wrap_mask);
}
FfxUInt32x3 FfxBrixelizerWrapCoords(FfxBrixelizerCascadeInfo cinfo, FfxUInt32x3 voxel_coord)
{
return (voxel_coord + cinfo.clipmap_offset) & FfxUInt32x3(FFX_BRIXELIZER_CASCADE_WRAP_MASK, FFX_BRIXELIZER_CASCADE_WRAP_MASK, FFX_BRIXELIZER_CASCADE_WRAP_MASK);
}
FfxUInt32 FfxBrixelizerLoadBrickIDUniform(FfxUInt32 voxel_flat_id, FfxUInt32 cascade_id)
{
return LoadCascadeBrickMapArrayUniform(cascade_id, voxel_flat_id);
}
FfxUInt32x3 FfxBrixelizerGetSDFAtlasOffset(FfxUInt32 brick_id)
{
FfxUInt32 dim = 8;
FfxUInt32 offset = FfxBrixelizerBrickGetIndex(brick_id);
FfxUInt32 bperdim = FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE / dim;
FfxUInt32 xoffset = offset % bperdim;
FfxUInt32 yoffset = (offset / bperdim) % bperdim;
FfxUInt32 zoffset = (offset / bperdim / bperdim);
return FfxUInt32x3(xoffset, yoffset, zoffset) * dim;
}
struct FfxBxAtlasBounds
{
FfxUInt32 brick_dim;
FfxFloat32x3 uvw_min;
FfxFloat32x3 uvw_max;
};
FfxBxAtlasBounds FfxBrixelizerGetAtlasBounds(FfxUInt32 brick_id)
{
FfxUInt32x3 brick_offset = FfxBrixelizerGetSDFAtlasOffset(brick_id);
FfxBxAtlasBounds bounds;
bounds.brick_dim = FfxUInt32(8);
bounds.uvw_min = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(0.5)) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
bounds.uvw_max = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(float(8.0 - 0.5))) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
return bounds;
}
FfxFloat32 FfxBrixelizerSampleBrixelDistance(FfxUInt32 brick_id, FfxFloat32x3 uvw)
{
FfxUInt32 brick_dim = FfxUInt32(8);
FfxFloat32 idim = FfxFloat32(1.0) / FfxFloat32(brick_dim);
FfxUInt32 texture_offset = FfxBrixelizerBrickGetIndex(brick_id);
// Offset for 7x7x7 region
FfxUInt32x3 brick_offset = FfxBrixelizerGetSDFAtlasOffset(brick_id);
FfxFloat32x3 uvw_min = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(0.5)) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
FfxFloat32x3 uvw_max = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(FfxFloat32(brick_dim - FfxFloat32(0.5)))) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
uvw = ffxLerp(uvw_min, uvw_max, uvw);
return SampleSDFAtlas(uvw);
}
FfxFloat32 FfxBrixelizerSampleBrixelDistance(FfxFloat32x3 uvw_min, FfxFloat32x3 uvw_max, FfxFloat32x3 uvw)
{
uvw = ffxLerp(uvw_min, uvw_max, uvw);
return SampleSDFAtlas(uvw);
}
FfxFloat32x3 FfxBrixelizerGetBrixelGrad(FfxFloat32x3 uvw_min, FfxFloat32x3 uvw_max, FfxFloat32x3 uvw) {
FfxFloat32 EPS = FfxFloat32(0.25) / (FfxFloat32(8.0) - FfxFloat32(1.0));
FfxFloat32x4 k = FfxFloat32x4(1.0, 1.0, 1.0, 0.0);
if (uvw.x > FfxFloat32(0.5)) k.x = -k.x;
if (uvw.y > FfxFloat32(0.5)) k.y = -k.y;
if (uvw.z > FfxFloat32(0.5)) k.z = -k.z;
FfxFloat32 fcenter = FfxBrixelizerSampleBrixelDistance(uvw_min, uvw_max, uvw);
FfxFloat32x3 grad = normalize(
k.xww * (FfxBrixelizerSampleBrixelDistance(uvw_min, uvw_max, uvw + k.xww * EPS) - fcenter) +
k.wyw * (FfxBrixelizerSampleBrixelDistance(uvw_min, uvw_max, uvw + k.wyw * EPS) - fcenter) +
k.wwz * (FfxBrixelizerSampleBrixelDistance(uvw_min, uvw_max, uvw + k.wwz * EPS) - fcenter)
);
if (any(isnan(grad))) {
return normalize(uvw - FFX_BROADCAST_FLOAT32X3(0.5));
}
return grad;
}
#endif // FFX_BRIXELIZER_BRICK_COMMON_H

View File

@@ -0,0 +1,58 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_BRICK_COMMON_PRIVATE_H
#define FFX_BRIXELIZER_BRICK_COMMON_PRIVATE_H
#include "ffx_brixelizer_common_private.h"
#include "ffx_brixelizer_brick_common.h"
FfxUInt32 FfxBrixelizerVoxelGetIndex(FfxUInt32 voxel_id)
{
return voxel_id & FFX_BRIXELIZER_VOXEL_ID_MASK;
}
FfxUInt32 FfxBrixelizerGetVoxelCascade(FfxUInt32 voxel_id)
{
return voxel_id >> FFX_BRIXELIZER_CASCADE_ID_SHIFT;
}
FfxUInt32 WrapFlatCoords(FfxBrixelizerCascadeInfo CINFO, FfxUInt32 voxel_idx)
{
return FfxBrixelizerFlattenPOT((FfxBrixelizerUnflattenPOT(voxel_idx, FFX_BRIXELIZER_CASCADE_DEGREE) + CINFO.clipmap_offset) & FFX_BROADCAST_UINT32X3(FFX_BRIXELIZER_CASCADE_WRAP_MASK), FFX_BRIXELIZER_CASCADE_DEGREE);
}
FfxUInt32 FfxBrixelizerMakeBrickID(FfxUInt32 offset)
{
return offset;
}
FfxFloat32x3 FfxBrixelizerGetBrixelGrad(FfxUInt32 brick_id, FfxFloat32x3 uvw)
{
FfxFloat32 EPS = FfxFloat32(0.1) / (FfxFloat32(8.0) - FfxFloat32(1.0));
FfxUInt32x3 brick_offset = FfxBrixelizerGetSDFAtlasOffset(brick_id);
FfxFloat32x3 uvw_min = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(0.5)) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
FfxFloat32x3 uvw_max = (FfxFloat32x3(brick_offset) + FFX_BROADCAST_FLOAT32X3(float(8.0 - float(0.5)))) / FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE;
return FfxBrixelizerGetBrixelGrad(uvw_min, uvw_max, uvw);
}
#endif // FFX_BRIXELIZER_BRICK_COMMON_PRIVATE_H

View File

@@ -0,0 +1,39 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_BUILD_COMMON_H
#define FFX_BRIXELIZER_BUILD_COMMON_H
#include "ffx_brixelizer_common_private.h"
void FfxBrixelizerMarkBrickFree(FfxUInt32 brick_id)
{
StoreBricksVoxelMap(FfxBrixelizerBrickGetIndex(brick_id), FFX_BRIXELIZER_INVALID_ID);
}
FfxUInt32 FfxBrixelizerLoadBrickVoxelID(FfxUInt32 brick_id)
{
return LoadBricksVoxelMap(FfxBrixelizerBrickGetIndex(brick_id));
}
// FfxUInt32 FfxBrixelizerLoadBrickCascadeID(FfxUInt32 brick_id) { return LoadBricksVoxelMap(FfxBrixelizerBrickGetIndex(brick_id)) >> FFX_BRIXELIZER_CASCADE_ID_SHIFT; }
#endif // FFX_BRIXELIZER_BUILD_COMMON_H

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,103 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_COMMON_H
#define FFX_BRIXELIZER_COMMON_H
#include "../ffx_core.h"
#include "ffx_brixelizer_host_gpu_shared.h"
#define ffxassert(x)
FfxBoolean FfxBrixelizerIsValidID(FfxUInt32 id)
{
return (id & FFX_BRIXELIZER_INVALID_ID) != FFX_BRIXELIZER_INVALID_ID;
}
FfxBoolean FfxBrixelizerIntersectAABB(FfxFloat32x3 ray_origin, FfxFloat32x3 ray_invdir, FfxFloat32x3 box_min, FfxFloat32x3 box_max, FFX_PARAMETER_OUT FfxFloat32 hit_min, FFX_PARAMETER_OUT FfxFloat32 hit_max)
{
FfxFloat32x3 tbot = ray_invdir * (box_min - ray_origin);
FfxFloat32x3 ttop = ray_invdir * (box_max - ray_origin);
FfxFloat32x3 tmin = min(ttop, tbot);
FfxFloat32x3 tmax = max(ttop, tbot);
FfxFloat32x2 t = max(tmin.xx, tmin.yz);
FfxFloat32 t0 = max(t.x, t.y);
t = min(tmax.xx, tmax.yz);
FfxFloat32 t1 = min(t.x, t.y);
hit_min = max(t0, FfxFloat32(0.0));
hit_max = max(t1, FfxFloat32(0.0));
return hit_max > hit_min;
}
FfxUInt32 FfxBrixelizerFlattenPOT(FfxUInt32x3 voxel_coord, FfxUInt32 degree)
{
return voxel_coord.x | (voxel_coord.y << degree) | (voxel_coord.z << (2 * degree));
}
FfxUInt32x3 FfxBrixelizerUnflattenPOT(FfxUInt32 flat_bx_coord, FfxUInt32 degree)
{
return FfxUInt32x3(flat_bx_coord & ((FfxUInt32(1) << degree) - 1), (flat_bx_coord >> degree) & ((FfxUInt32(1) << degree) - 1), flat_bx_coord >> (2 * degree));
}
FfxUInt32 FfxBrixelizerPackUnsigned8Bits(FfxFloat32 a)
{
return FfxUInt32(ffxSaturate(a) * FfxFloat32(255.0)) & 0xffu;
}
FfxFloat32 FfxBrixelizerUnpackUnsigned8Bits(FfxUInt32 uval)
{
return FfxFloat32(uval & 0xff) / FfxFloat32(255.0);
}
FfxUInt32 PackUVWC(FfxFloat32x4 uvwc)
{
FfxUInt32 pack = //
(FfxBrixelizerPackUnsigned8Bits(uvwc.x) << 0) //
| (FfxBrixelizerPackUnsigned8Bits(uvwc.y) << 8) //
| (FfxBrixelizerPackUnsigned8Bits(uvwc.z) << 16) //
| (FfxBrixelizerPackUnsigned8Bits(uvwc.w) << 24); //
return pack;
}
FfxFloat32x2 FfxBrixelizerOctahedronToUV(FfxFloat32x3 N)
{
N.xy = FfxFloat32x2(N.xy) / dot(FFX_BROADCAST_FLOAT32X3(1.0), abs(N));
FfxFloat32x2 k;
k.x = N.x >= float(0.0) ? float(1.0) : -float(1.0);
k.y = N.y >= float(0.0) ? float(1.0) : -float(1.0);
if (N.z <= float(0.0)) N.xy = (float(1.0) - abs(FfxFloat32x2(N.yx))) * k;
return N.xy * float(0.5) + float(0.5);
}
FfxFloat32x3 FfxBrixelizerUVToOctahedron(FfxFloat32x2 UV)
{
UV = float(2.0) * (UV - float(0.5));
FfxFloat32x3 N = FfxFloat32x3(UV, float(1.0) - abs(UV.x) - abs(UV.y));
float t = max(-N.z, float(0.0));
FfxFloat32x2 k;
k.x = N.x >= float(0.0) ? -t : t;
k.y = N.y >= float(0.0) ? -t : t;
N.xy += k;
return normalize(N);
}
#endif // FFX_BRIXELIZER_COMMON_H

View File

@@ -0,0 +1,243 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_COMMON_PRIVATE_H
#define FFX_BRIXELIZER_COMMON_PRIVATE_H
#include "ffx_brixelizer_common.h"
#include "ffx_brixelizer_host_gpu_shared_private.h"
FfxInt32x3 to_int3(FfxUInt32x3 a) { return FfxInt32x3(FfxInt32(a.x), FfxInt32(a.y), FfxInt32(a.z)); }
FfxFloat32x3 to_float3(FfxUInt32x3 a) { return FfxFloat32x3(FfxFloat32(a.x), FfxFloat32(a.y), FfxFloat32(a.z)); }
FfxFloat32x3 to_float3(FfxInt32x3 a) { return FfxFloat32x3(FfxFloat32(a.x), FfxFloat32(a.y), FfxFloat32(a.z)); }
FfxUInt32 FfxBrixelizerPackDistance(FfxFloat32 distance)
{
FfxUInt32 uval = ffxAsUInt32(distance);
FfxUInt32 sign_bit = uval >> FfxUInt32(31);
return (uval << 1) | sign_bit;
}
FfxFloat32 FfxBrixelizerUnpackDistance(FfxUInt32 uval)
{
FfxUInt32 sign_bit = (uval & 1);
return ffxAsFloat((uval >> FfxUInt32(1)) | (sign_bit << FfxUInt32(31)));
}
// Returns the minimal absolute value with its sign unchanged
FfxFloat32 FfxBrixelizerUnsignedMin(FfxFloat32 a, FfxFloat32 b) { return abs(a) < abs(b) ? a : b; }
// sign without zero
FfxFloat32 FfxBrixelizerGetSign(FfxFloat32 v) { return v < FfxFloat32(0.0) ? -FfxFloat32(1.0) : FfxFloat32(1.0); }
FfxFloat32 dot2(FfxFloat32x3 v) { return dot(v, v); }
FfxFloat32 dot2(FfxFloat32x2 v) { return dot(v, v); }
// https://www.shadertoy.com/view/4sXXRN
// The MIT License
// Copyright © 2014 Inigo Quilez
// 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.
//
// It computes the distance to a triangle.
//
// See here: http://iquilezles.org/www/articles/triangledistance/triangledistance.htm
//
// In case a mesh was rendered, only one square root would be needed for the
// whole mesh. In this example the triangle is given a thckness of 0.01 units
// for display purposes. Like the square root, this thickness should be added
// only once for the whole mesh too.
FfxFloat32 CalculateDistanceToTriangle(FfxFloat32x3 p, FfxFloat32x3 a, FfxFloat32x3 b, FfxFloat32x3 c) {
FfxFloat32x3 ba = b - a;
FfxFloat32x3 pa = p - a;
FfxFloat32x3 cb = c - b;
FfxFloat32x3 pb = p - b;
FfxFloat32x3 ac = a - c;
FfxFloat32x3 pc = p - c;
FfxFloat32x3 nor = cross(ba, ac);
return sqrt( //
(sign(dot(cross(ba, nor), pa)) + //
sign(dot(cross(cb, nor), pb)) + //
sign(dot(cross(ac, nor), pc)) < //
FfxFloat32(2.0)) //
? //
min(min( //
dot2(ba * ffxSaturate(dot(ba, pa) / dot2(ba)) - pa), //
dot2(cb * ffxSaturate(dot(cb, pb) / dot2(cb)) - pb)), //
dot2(ac * ffxSaturate(dot(ac, pc) / dot2(ac)) - pc)) //
: //
dot(nor, pa) * dot(nor, pa) / dot2(nor) //
); //
}
FfxFloat32 CalculateDistanceToTriangleSquared(FfxFloat32x3 ba, //
FfxFloat32x3 pa, //
FfxFloat32x3 cb, //
FfxFloat32x3 pb, //
FfxFloat32x3 ac, //
FfxFloat32x3 pc, //
FfxFloat32x3 nor, //
FfxFloat32x3 cross_ba_nor, //
FfxFloat32x3 cross_cb_nor, //
FfxFloat32x3 cross_ac_nor, //
FfxFloat32 dot2_ba, //
FfxFloat32 dot2_cb, //
FfxFloat32 dot2_ac, //
FfxFloat32 dot2_nor //
) {
return //
( //
(sign(dot(cross_ba_nor, pa)) + //
sign(dot(cross_cb_nor, pb)) + //
sign(dot(cross_ac_nor, pc)) < //
FfxFloat32(2.0)) //
? //
min(min( //
dot2(ba * ffxSaturate(dot(ba, pa) / dot2_ba) - pa), //
dot2(cb * ffxSaturate(dot(cb, pb) / dot2_cb) - pb)), //
dot2(ac * ffxSaturate(dot(ac, pc) / dot2_ac) - pc)) //
: //
dot(nor, pa) * dot(nor, pa) / dot2_nor //
); //
}
#define brixelizerreal FfxFloat32
#define brixelizerreal2 FfxFloat32x2
#define brixelizerreal3 FfxFloat32x3
#define brixelizerreal4 FfxFloat32x4
#ifdef FFX_HLSL
# define brixelizerreal3x2 float3x2
#else
# define brixelizerreal3x2 mat3x2
#endif
brixelizerreal3 to_brixelizerreal3(FfxUInt32x3 a) { return brixelizerreal3(brixelizerreal(a.x), brixelizerreal(a.y), brixelizerreal(a.z)); }
// Offsets the plane equation for the nearest grid point
brixelizerreal FfxBrixelizerOffsetByMax(brixelizerreal de, brixelizerreal2 ne, brixelizerreal offset)
{
de += max(FfxFloat32(0.0), ne.x) + abs(ne.x * offset);
de += max(FfxFloat32(0.0), ne.y) + abs(ne.y * offset);
return de;
}
// Offsets the plane equation for the next grid point
brixelizerreal FfxBrixelizerOffsetByMin(brixelizerreal de, brixelizerreal2 ne, brixelizerreal offset)
{
de += min(FfxFloat32(0.0), ne.x) - abs(ne.x * offset);
de += min(FfxFloat32(0.0), ne.y) - abs(ne.x * offset);
return de;
}
void FfxBrixelizerGet2DEdge(FFX_PARAMETER_OUT brixelizerreal2 ne,
FFX_PARAMETER_OUT brixelizerreal de,
brixelizerreal orientation,
brixelizerreal edge_x,
brixelizerreal edge_y,
brixelizerreal vertex_x,
brixelizerreal vertex_y,
brixelizerreal offset)
{
ne = brixelizerreal2(-orientation * edge_y, orientation * edge_x);
de = -(ne.x * vertex_x + ne.y * vertex_y);
de = FfxBrixelizerOffsetByMax(de, ne, offset);
}
void FfxBrixelizerGet2DEdges(
FFX_PARAMETER_OUT brixelizerreal3 de_xy,
FFX_PARAMETER_OUT brixelizerreal3x2 ne_xy,
FFX_PARAMETER_OUT brixelizerreal3 de_xz,
FFX_PARAMETER_OUT brixelizerreal3x2 ne_xz,
FFX_PARAMETER_OUT brixelizerreal3 de_yz,
FFX_PARAMETER_OUT brixelizerreal3x2 ne_yz,
FFX_PARAMETER_OUT brixelizerreal3 gn,
brixelizerreal3 TRIANGLE_VERTEX_0,
brixelizerreal3 TRIANGLE_VERTEX_1,
brixelizerreal3 TRIANGLE_VERTEX_2,
brixelizerreal offset,
bool invert)
{
brixelizerreal3 e0 = TRIANGLE_VERTEX_1.xyz - TRIANGLE_VERTEX_0.xyz;
brixelizerreal3 e1 = TRIANGLE_VERTEX_2.xyz - TRIANGLE_VERTEX_1.xyz;
brixelizerreal3 e2 = TRIANGLE_VERTEX_0.xyz - TRIANGLE_VERTEX_2.xyz;
if (invert) {
e0 = -e0;
e1 = -e1;
e2 = -e2;
}
gn = normalize(cross(e2, e0));
brixelizerreal orientation_xy = brixelizerreal(gn.z < FfxFloat32(0.0) ? -FfxFloat32(1.0) : FfxFloat32(1.0));
FfxBrixelizerGet2DEdge(/* out */ ne_xy[0], /* out */ de_xy[0], orientation_xy, e0.x, e0.y, TRIANGLE_VERTEX_0.x, TRIANGLE_VERTEX_0.y, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_xy[1], /* out */ de_xy[1], orientation_xy, e1.x, e1.y, TRIANGLE_VERTEX_1.x, TRIANGLE_VERTEX_1.y, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_xy[2], /* out */ de_xy[2], orientation_xy, e2.x, e2.y, TRIANGLE_VERTEX_2.x, TRIANGLE_VERTEX_2.y, offset);
brixelizerreal orientation_xz = brixelizerreal(gn.y > FfxFloat32(0.0) ? -FfxFloat32(1.0) : FfxFloat32(1.0));
FfxBrixelizerGet2DEdge(/* out */ ne_xz[0], /* out */ de_xz[0], orientation_xz, e0.x, e0.z, TRIANGLE_VERTEX_0.x, TRIANGLE_VERTEX_0.z, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_xz[1], /* out */ de_xz[1], orientation_xz, e1.x, e1.z, TRIANGLE_VERTEX_1.x, TRIANGLE_VERTEX_1.z, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_xz[2], /* out */ de_xz[2], orientation_xz, e2.x, e2.z, TRIANGLE_VERTEX_2.x, TRIANGLE_VERTEX_2.z, offset);
brixelizerreal orientation_yz = brixelizerreal(gn.x < FfxFloat32(0.0) ? -FfxFloat32(1.0) : FfxFloat32(1.0));
FfxBrixelizerGet2DEdge(/* out */ ne_yz[0], /* out */ de_yz[0], orientation_yz, e0.y, e0.z, TRIANGLE_VERTEX_0.y, TRIANGLE_VERTEX_0.z, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_yz[1], /* out */ de_yz[1], orientation_yz, e1.y, e1.z, TRIANGLE_VERTEX_1.y, TRIANGLE_VERTEX_1.z, offset);
FfxBrixelizerGet2DEdge(/* out */ ne_yz[2], /* out */ de_yz[2], orientation_yz, e2.y, e2.z, TRIANGLE_VERTEX_2.y, TRIANGLE_VERTEX_2.z, offset);
}
bool FfxBrixelizerEvalEdge(brixelizerreal2 vertex, brixelizerreal3 de, brixelizerreal3x2 ne)
{
return (ne[0].x * vertex.x + ne[0].y * vertex.y + de[0] >= brixelizerreal(FfxFloat32(0.0))) &&
(ne[1].x * vertex.x + ne[1].y * vertex.y + de[1] >= brixelizerreal(FfxFloat32(0.0))) &&
(ne[2].x * vertex.x + ne[2].y * vertex.y + de[2] >= brixelizerreal(FfxFloat32(0.0)));
}
// We need those non-POT versions around for culling
FfxUInt32x3 FfxBrixelizerUnflatten(FfxUInt32 flat_bx_coord, FfxUInt32x3 dim)
{
return FfxUInt32x3(flat_bx_coord % dim.x, (flat_bx_coord / dim.x) % dim.y, flat_bx_coord / (dim.x * dim.y));
}
FfxUInt32x2 FfxBrixelizerUnflatten(FfxUInt32 flat_bx_coord, FfxUInt32x2 dim)
{
return FfxUInt32x2(flat_bx_coord % dim.x, (flat_bx_coord / dim.x) % dim.y);
}
FfxUInt32 FfxBrixelizerFlatten(FfxUInt32x3 voxel_coord, FfxUInt32x3 dim)
{
return voxel_coord.x + voxel_coord.y * dim.x + voxel_coord.z * dim.x * dim.y;
}
#endif // FFX_BRIXELIZER_COMMON_PRIVATE_H

View File

@@ -0,0 +1,306 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_CONTEXT_OPS_H
#define FFX_BRIXELIZER_CONTEXT_OPS_H
#include "ffx_brixelizer_host_gpu_shared_private.h"
#include "ffx_brixelizer_brick_common_private.h"
#include "ffx_brixelizer_build_common.h"
#include "ffx_brixelizer_common_private.h"
void FfxBrixelizerAppendClearBrick(FfxUInt32 brick_id)
{
FfxUInt32 offset;
IncrementContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_CLEAR_BRICKS, FfxUInt32(1), offset);
StoreBricksClearList(offset, brick_id);
}
void FfxBrixelizerAppendDirtyBrick(FfxUInt32 brick_id)
{
FfxUInt32 offset;
IncrementContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_DIRTY_BRICKS, FfxUInt32(1), offset);
StoreBricksDirtyList(offset, brick_id);
}
void FfxBrixelizerClearCounters()
{
for (FfxUInt32 i = 0; i < FFX_BRIXELIZER_NUM_CONTEXT_COUNTERS; i++) {
StoreContextCounter(i, FfxUInt32(0));
}
}
void FfxBrixelizerAddBrickToFreeList(FfxUInt32 brick_id)
{
FfxUInt32 offset;
IncrementContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_FREE_BRICKS, FfxUInt32(1), offset);
StoreBricksFreeList(offset, brick_id);
}
FfxFloat32 FfxBrixelizerLoadBrixelDist(FfxUInt32 brick_id, FfxInt32x3 coord)
{
if (any(FFX_GREATER_THAN(coord, FFX_BROADCAST_INT32X3(7))) || any(FFX_LESS_THAN(coord, FFX_BROADCAST_INT32X3(0)))) return FfxFloat32(1.0);
return LoadSDFAtlas(FfxBrixelizerGetSDFAtlasOffset(brick_id) + coord);
}
void FfxBrixelizerStoreBrixelDist(FfxUInt32 brick_id, FfxUInt32x3 coord, FfxFloat32 dist)
{
StoreSDFAtlas(FfxBrixelizerGetSDFAtlasOffset(brick_id) + coord, clamp(dist, FfxFloat32(0.0), FfxFloat32(1.0)));
}
void FfxBrixelizerClearBrick(FfxUInt32 brick_id, FfxUInt32x3 voxel_coord)
{
FfxBrixelizerStoreBrixelDist(brick_id, voxel_coord, FfxFloat32(1.0));
}
FfxFloat32 FfxBrixelizerEikonalMin(FfxFloat32 a, FfxFloat32 b)
{
return FfxBrixelizerUnsignedMin(a, b);
}
// Eikonal solver, enforce |gradient|==d
// Generic form for n dimensions:
// U = 1/n * (sum(U_i) + sqrt(sum(U_i) * sum(U_i) - n * (sum(U_i * U_i) - D)))
// when sqrt is non real then fall back to min of lower dimensions
FfxFloat32 FfxBrixelizerEikonal1D(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z, FfxFloat32 d)
{
FfxFloat32 xyz = FfxBrixelizerEikonalMin(x, FfxBrixelizerEikonalMin(y, z));
return (xyz + d * FfxBrixelizerGetSign(xyz));
}
FfxFloat32 FfxBrixelizerEikonal2D(FfxFloat32 x, FfxFloat32 y, FfxFloat32 d)
{
const FfxFloat32 k = FfxFloat32(1.0) / FfxFloat32(2.0);
FfxFloat32 xy = x + y;
FfxFloat32 v = xy * xy - FfxFloat32(2.0) * (x * x + y * y - d * d);
if (v < FfxFloat32(0.0)) return FfxFloat32(1.0);
return k * (xy + sqrt(v) * FfxBrixelizerGetSign(xy));
}
FfxFloat32 FfxBrixelizerEikonal3D(FfxFloat32 x, FfxFloat32 y, FfxFloat32 z, FfxFloat32 d)
{
const FfxFloat32 k = FfxFloat32(1.0) / FfxFloat32(3.0);
FfxFloat32 xyz = x + y + z;
FfxFloat32 v = xyz * xyz - FfxFloat32(3.0) * (x * x + y * y + z * z - d * d);
if (v < FfxFloat32(0.0)) return FfxFloat32(1.0);
return k * (xyz + sqrt(v) * FfxBrixelizerGetSign(xyz));
}
FFX_GROUPSHARED FfxFloat32 lds_eikonal_sdf_cache[512];
FfxFloat32 FfxBrixelizerLDSLoadSDF(FfxInt32x3 coord)
{
if (any(FFX_LESS_THAN(coord, FFX_BROADCAST_INT32X3(0))) || any(FFX_GREATER_THAN(coord, FFX_BROADCAST_INT32X3(7)))) return FfxFloat32(1.0);
return lds_eikonal_sdf_cache[FfxUInt32(coord.x) + FfxUInt32(coord.y) * FfxUInt32(8) + FfxUInt32(coord.z) * FfxUInt32(8 * 8)];
}
void FfxBrixelizerLDSStoreSDF(FfxInt32x3 coord, FfxFloat32 sdf)
{
lds_eikonal_sdf_cache[FfxUInt32(coord.x) + FfxUInt32(coord.y) * FfxUInt32(8) + FfxUInt32(coord.z) * FfxUInt32(8 * 8)] = sdf;
}
// Collects a list of bricks to clear for indirect args
// those don't have a brick_id->voxel_id mapping are considered free
// those that got eikonal counter are considered dirty as they had something baked in them
// DEFINE_ENTRY(64, 1, 1, CollectClearBricks)
void FfxBrixelizerCollectClearBricks(FfxUInt32 brick_offset)
{
if (brick_offset >= GetContextInfoNumBricks()) return;
FfxUInt32 brick_id = FfxBrixelizerMakeBrickID(brick_offset);
FfxUInt32 dim = 8;
FfxUInt32 voxel_id = FfxBrixelizerLoadBrickVoxelID(brick_id);
FfxUInt32 voxel_idx = FfxBrixelizerVoxelGetIndex(voxel_id);
FfxUInt32 cascade_id = FfxBrixelizerGetVoxelCascade(voxel_id);
FfxUInt32 eikonal_counter = LoadBricksEikonalCounters(brick_id);
// @TODO cleanup this mess
if (GetBuildInfoDoInitialization() > 0) {
StoreBricksEikonalCounters(brick_id, 0);
FfxBrixelizerMarkBrickFree(brick_id);
FfxBrixelizerAddBrickToFreeList(brick_id);
FfxBrixelizerAppendClearBrick(brick_id);
} else {
#ifdef FFX_BRIXELIZER_DEBUG_FORCE_REBUILD
StoreBricksEikonalCounters(brick_id, 0);
FfxBrixelizerMarkBrickFree(brick_id);
FfxBrixelizerAddBrickToFreeList(brick_id);
FfxBrixelizerAppendClearBrick(brick_id);
#else // !FFX_BRIXELIZER_DEBUG_FORCE_REBUILD
if (FfxBrixelizerIsInvalidID(voxel_id)) {
FfxBrixelizerAddBrickToFreeList(brick_id);
if (eikonal_counter > 0) { // Means there's been some baking using this brick so need to clear
FfxBrixelizerAppendClearBrick(brick_id);
StoreBricksEikonalCounters(brick_id, 0);
}
}
#endif // !FFX_BRIXELIZER_DEBUG_FORCE_REBUILD
}
}
void FfxBrixelizerPrepareClearBricks()
{
FfxUInt32 tier_cnt = LoadContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_CLEAR_BRICKS);
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_CLEAR_BRICKS_32 + FfxUInt32(0), tier_cnt * FfxUInt32(1 << (3)));
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_CLEAR_BRICKS_32 + FfxUInt32(1), FfxUInt32(1));
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_CLEAR_BRICKS_32 + FfxUInt32(2), FfxUInt32(1));
}
void FfxBrixelizerClearBrickEntry(FfxUInt32 gtid, FfxUInt32 group_id)
{
FfxUInt32 brick_offset = group_id.x >> (3);
FfxUInt32 stamp_id = group_id.x & ((1 << (3)) - 1);
FfxUInt32 brick_id = LoadBricksClearList(brick_offset);
FfxUInt32 brick_dim = 8;
FfxUInt32x3 local_coord = FfxBrixelizerUnflattenPOT(gtid, 2) + FfxBrixelizerUnflattenPOT(stamp_id, 1) * FfxUInt32(4);
FfxBrixelizerClearBrick(brick_id, local_coord);
}
void FfxBrixelizerCollectDirtyBricks(FfxUInt32 brick_offset)
{
FfxUInt32 brick_id = FfxBrixelizerMakeBrickID(brick_offset);
FfxUInt32 eikonal_counter = LoadBricksEikonalCounters(brick_id);
FfxUInt32 dim = 8;
FfxUInt32 voxel_id = FfxBrixelizerLoadBrickVoxelID(brick_id);
if (FfxBrixelizerIsValidID(voxel_id)) {
FfxUInt32 voxel_idx = FfxBrixelizerVoxelGetIndex(voxel_id);
FfxUInt32 cascade_idx = FfxBrixelizerGetVoxelCascade(voxel_id);
FfxUInt32x3 clipmap_offset = GetContextInfoCascadeClipmapOffset(cascade_idx);
FfxUInt32x3 voxel_offset = FfxBrixelizerUnflattenPOT(voxel_idx, FFX_BRIXELIZER_CASCADE_DEGREE);
FfxUInt32 wrapped_voxel_idx = FfxBrixelizerFlattenPOT(FfxBrixelizerWrapCoords(FfxInt32x3(clipmap_offset), FFX_BRIXELIZER_CASCADE_WRAP_MASK, voxel_offset), FFX_BRIXELIZER_CASCADE_DEGREE);
if (eikonal_counter < 16) {
FfxBrixelizerAppendDirtyBrick(brick_id);
StoreBricksEikonalCounters(brick_id, eikonal_counter + 16);
}
}
}
// DEFINE_ENTRY(512, 1, 1, Eikonal)
void FfxBrixelizerEikonal(FfxUInt32 local_coord_packed, FfxUInt32 brick_offset)
{
FfxUInt32 brick_id = LoadBricksDirtyList(brick_offset);
FfxUInt32 voxel_id = FfxBrixelizerLoadBrickVoxelID(brick_id);
FfxUInt32x3 local_coord = FfxBrixelizerUnflattenPOT(local_coord_packed, 3);
if (FfxBrixelizerIsValidID(voxel_id)) {
FfxFloat32 cell_distance = FfxFloat32(1.0) / (FfxFloat32(8));
FfxFloat32 e = FfxBrixelizerLoadBrixelDist(brick_id, FfxInt32x3(local_coord));
FfxBrixelizerLDSStoreSDF(FfxInt32x3(local_coord), e);
FFX_GROUP_MEMORY_BARRIER;
for (FfxUInt32 j = FfxUInt32(0); j < FfxUInt32(3); j++) {
for (FfxUInt32 i = FfxUInt32(0); i < FfxUInt32(4); i++) {
FfxUInt32 d = FfxUInt32(1) << (FfxUInt32(3) - i);
FfxFloat32x3 min_dists = FFX_BROADCAST_FLOAT32X3(FfxFloat32(1.0));
min_dists.x = FfxBrixelizerEikonalMin(FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(d, 0, 0)), FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(-d, 0, 0)));
min_dists.y = FfxBrixelizerEikonalMin(FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(0, d, 0)), FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(0, -d, 0)));
min_dists.z = FfxBrixelizerEikonalMin(FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(0, 0, d)), FfxBrixelizerLDSLoadSDF(FfxInt32x3(local_coord) + FfxInt32x3(0, 0, -d)));
min_dists = abs(min_dists);
FfxFloat32 e10 = FfxBrixelizerEikonal1D(min_dists.x, min_dists.y, min_dists.z, d * cell_distance);
FfxFloat32 e20 = FfxBrixelizerEikonal2D(min_dists.x, min_dists.y, d * cell_distance);
FfxFloat32 e21 = FfxBrixelizerEikonal2D(min_dists.x, min_dists.z, d * cell_distance);
FfxFloat32 e22 = FfxBrixelizerEikonal2D(min_dists.z, min_dists.y, d * cell_distance);
FfxFloat32 e30 = FfxBrixelizerEikonal3D(min_dists.x, min_dists.y, min_dists.z, d * cell_distance);
e = FfxBrixelizerEikonalMin(e, FfxBrixelizerEikonalMin(e10, FfxBrixelizerEikonalMin(e20, FfxBrixelizerEikonalMin(e21, FfxBrixelizerEikonalMin(e22, e30)))));
e = FfxBrixelizerEikonalMin(e, e10);
FfxBrixelizerLDSStoreSDF(FfxInt32x3(local_coord), e);
}
}
FfxBrixelizerStoreBrixelDist(brick_id, local_coord, e);
}
}
void FfxBrixelizerMergeBricks(FfxUInt32 gtid, FfxUInt32 group_id)
{
FfxUInt32 cnt = LoadContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_MERGE_BRICKS);
FfxUInt32 merge_idx = group_id / FfxUInt32(8);
FfxUInt32 stamp_idx = group_id % FfxUInt32(8);
FfxUInt32 voxel_idx = gtid + stamp_idx * FfxUInt32(64);
FfxUInt32x3 voxel_offset = FfxBrixelizerUnflattenPOT(voxel_idx, FfxUInt32(3));
if (merge_idx >= cnt) return;
FfxUInt32 brick_A = LoadBricksMergeList(merge_idx * FfxUInt32(2) + FfxUInt32(0));
FfxUInt32 brick_B = LoadBricksMergeList(merge_idx * FfxUInt32(2) + FfxUInt32(1));
FfxUInt32x3 atlas_offset_A = FfxBrixelizerGetSDFAtlasOffset(brick_A);
FfxUInt32x3 atlas_offset_B = FfxBrixelizerGetSDFAtlasOffset(brick_B);
{
FfxFloat32 sdf_val_A = LoadSDFAtlas(atlas_offset_A + voxel_offset);
FfxFloat32 sdf_val_B = LoadSDFAtlas(atlas_offset_B + voxel_offset);
StoreSDFAtlas(atlas_offset_B + voxel_offset, min(sdf_val_A, sdf_val_B));
}
}
void FfxBrixelizerMergeCascades(FfxUInt32 voxel_idx)
{
// Assume the same clipmap state so no need to wrap the coords
FfxUInt32 brick_A = LoadCascadeBrickMapArrayUniform(GetBuildInfo().src_cascade_A, voxel_idx);
FfxUInt32 brick_B = LoadCascadeBrickMapArrayUniform(GetBuildInfo().src_cascade_B, voxel_idx);
if (FfxBrixelizerIsValidID(brick_A) && FfxBrixelizerIsValidID(brick_B)) {
FfxUInt32 offset;
IncrementContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_MERGE_BRICKS, FfxUInt32(1), /* out */ offset);
StoreBricksMergeList(offset * FfxUInt32(2) + FfxUInt32(0), brick_A);
StoreBricksMergeList(offset * FfxUInt32(2) + FfxUInt32(1), brick_B);
StoreCascadeBrickMapArrayUniform(GetBuildInfo().dst_cascade, voxel_idx, brick_B); // we store the 2nd brick and merge into it
FfxUInt32 brick_A_aabb_pack = LoadBricksAABB(FfxBrixelizerBrickGetIndex(brick_A));
FfxUInt32x3 brick_A_aabb_umin = FfxBrixelizerUnflattenPOT(brick_A_aabb_pack & ((FfxUInt32(1) << FfxUInt32(9)) - FfxUInt32(1)), FfxUInt32(3));
FfxUInt32x3 brick_A_aabb_umax = FfxBrixelizerUnflattenPOT((brick_A_aabb_pack >> FfxUInt32(9)) & ((FfxUInt32(1) << FfxUInt32(9)) - FfxUInt32(1)), FfxUInt32(3));
FfxUInt32 brick_B_aabb_pack = LoadBricksAABB(FfxBrixelizerBrickGetIndex(brick_B));
FfxUInt32x3 brick_B_aabb_umin = FfxBrixelizerUnflattenPOT(brick_B_aabb_pack & ((FfxUInt32(1) << FfxUInt32(9)) - FfxUInt32(1)), FfxUInt32(3));
FfxUInt32x3 brick_B_aabb_umax = FfxBrixelizerUnflattenPOT((brick_B_aabb_pack >> FfxUInt32(9)) & ((FfxUInt32(1) << FfxUInt32(9)) - FfxUInt32(1)), FfxUInt32(3));
brick_B_aabb_umin = min(brick_A_aabb_umin, brick_B_aabb_umin);
brick_B_aabb_umax = max(brick_A_aabb_umax, brick_B_aabb_umax);
brick_B_aabb_pack = FfxBrixelizerFlattenPOT(brick_B_aabb_umin, 3) | (FfxBrixelizerFlattenPOT(brick_B_aabb_umax, 3) << 9);
StoreBricksAABB(FfxBrixelizerBrickGetIndex(brick_B), brick_B_aabb_pack);
} else if (brick_B == FFX_BRIXELIZER_UNINITIALIZED_ID || brick_A == FFX_BRIXELIZER_UNINITIALIZED_ID) {
StoreCascadeBrickMapArrayUniform(GetBuildInfo().dst_cascade, voxel_idx, FFX_BRIXELIZER_UNINITIALIZED_ID);
} else if (FfxBrixelizerIsValidID(brick_B)) {
StoreCascadeBrickMapArrayUniform(GetBuildInfo().dst_cascade, voxel_idx, brick_B);
} else {
StoreCascadeBrickMapArrayUniform(GetBuildInfo().dst_cascade, voxel_idx, brick_A);
}
}
void FfxBrixelizerPrepareEikonalArgs()
{
FfxUInt32 tier_cnt = LoadContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_DIRTY_BRICKS);
StoreContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_DIRTY_BRICKS, 0);
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_EIKONAL_32 + 0, tier_cnt);
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_EIKONAL_32 + FfxUInt32(1), 1);
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_EIKONAL_32 + 2, 1);
}
void FfxBrixelizerPrepareMergeBricksArgs()
{
FfxUInt32 cnt = LoadContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_MERGE_BRICKS);
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_MERGE_BRICKS_32 + FfxUInt32(0), cnt * FfxUInt32(8)); // 2^3 of 4^3 lanes
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_MERGE_BRICKS_32 + FfxUInt32(1), FfxUInt32(1));
StoreIndirectArgs(FFX_BRIXELIZER_INDIRECT_OFFSETS_MERGE_BRICKS_32 + FfxUInt32(2), FfxUInt32(1));
}
#endif // ifndef FFX_BRIXELIZER_CONTEXT_OPS_H

View File

@@ -0,0 +1,105 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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 "ffx_brixelizer_common.h"
#include "ffx_brixelizer_host_gpu_shared.h"
void PushAABB(FfxBrixelizerDebugAABB aabb)
{
FfxUInt32 offset;
IncrementContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_NUM_DEBUG_AABBS, 1, offset);
if (offset < GetDebugInfoMaxAABBs()) {
StoreDebugAABB(offset, aabb);
}
}
void FfxBrixelizerDrawAABBTreeAABB(FfxUInt32 aabb_index)
{
// if g_debug_info.debug_state is set to 1 we only want to draw the cascade bounding box
if (GetDebugInfoDebugState() == 1 && aabb_index > 0) {
return;
}
if (aabb_index == 0) {
FfxBrixelizerCascadeInfo cascade_info = GetCascadeInfo();
FfxBrixelizerDebugAABB debug_aabb;
debug_aabb.color = FfxFloat32x3(1.0f, 0.0f, 0.0f);
debug_aabb.aabbMin = cascade_info.grid_min;
debug_aabb.aabbMax = cascade_info.grid_max;
PushAABB(debug_aabb);
} else if (aabb_index == 1) {
// root aabb
FfxUInt32 index = 16*16*16 + 4*4*4*6;
FfxBrixelizerDebugAABB debug_aabb;
debug_aabb.color = FfxFloat32x3(0.0f, 1.0f, 1.0f);
debug_aabb.aabbMin = LoadCascadeAABBTreeFloat3(index);
debug_aabb.aabbMax = LoadCascadeAABBTreeFloat3(index + 3);
PushAABB(debug_aabb);
} else if (aabb_index <= 1 + 4*4*4) {
// second level aabb
FfxUInt32 index = 16*16*16 + (aabb_index - 2)*6;
FfxBrixelizerDebugAABB debug_aabb;
debug_aabb.color = FfxFloat32x3(1.0f, 1.0f, 0.0f);
debug_aabb.aabbMin = LoadCascadeAABBTreeFloat3(index);
debug_aabb.aabbMax = LoadCascadeAABBTreeFloat3(index + 3);
if (ffxAsUInt32(debug_aabb.aabbMin.x) == ffxAsUInt32(debug_aabb.aabbMax.x)) {
return;
}
PushAABB(debug_aabb);
} else if (aabb_index <= 1 + 4*4*4 + 16*16*16) {
// leaf aabb
FfxBrixelizerCascadeInfo cascade_info = GetCascadeInfo();
FfxUInt32 index = aabb_index - (4*4*4 + 1 + 1);
FfxUInt32 packedAABB = LoadCascadeAABBTreeUInt(index);
if (packedAABB == FFX_BRIXELIZER_INVALID_BOTTOM_AABB_NODE) {
return;
}
FfxUInt32x3 leaf_offset = FfxBrixelizerUnflattenPOT(index, 4) * 32;
FfxUInt32x3 aabbMin = leaf_offset + FfxBrixelizerUnflattenPOT(packedAABB & ((1 << 15) - 1), 5);
FfxUInt32x3 aabbMax = leaf_offset + FfxBrixelizerUnflattenPOT((packedAABB >> 16) & ((1 << 15) - 1), 5) + 1;
FfxBrixelizerDebugAABB debug_aabb;
debug_aabb.color = FfxFloat32x3(0.0f, 1.0f, 0.0f);
debug_aabb.aabbMin = cascade_info.grid_min + FfxFloat32x3(aabbMin) * cascade_info.voxel_size / 8.0f;
debug_aabb.aabbMax = cascade_info.grid_min + FfxFloat32x3(aabbMax) * cascade_info.voxel_size / 8.0f;
PushAABB(debug_aabb);
}
}
void FfxBrixelizerDrawInstanceAABB(FfxUInt32 index)
{
// XXX -- use debug_state to pass in number of instance IDs
if (index >= GetDebugInfoDebugState()) {
return;
}
FfxUInt32 instance_id = GetDebugInstanceID(index);
FfxBrixelizerInstanceInfo instance_info = LoadInstanceInfo(instance_id);
FfxBrixelizerDebugAABB debug_aabb;
debug_aabb.color = FfxFloat32x3(0.0f, 0.0f, 1.0f);
debug_aabb.aabbMin = instance_info.aabbMin;
debug_aabb.aabbMax = instance_info.aabbMax;
PushAABB(debug_aabb);
}

View File

@@ -0,0 +1,199 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_DEBUG_VISUALIZATION_H
#define FFX_BRIXELIZER_DEBUG_VISUALIZATION_H
#include "ffx_brixelizer_host_gpu_shared.h"
#define FFX_BRIXELIZER_TRAVERSAL_EPS (GetDebugInfoPreviewSDFSolveEpsilon() / FfxFloat32(8.0))
#include "ffx_brixelizer_trace_ops.h"
// XXX -- tmp
FfxUInt32 FfxBrixelizerGetVoxelCascade(FfxUInt32 voxel_id)
{
return voxel_id >> FFX_BRIXELIZER_CASCADE_ID_SHIFT;
}
#define FLT_INF 1e30f
// By Morgan McGuire @morgan3d, http://graphicscodex.com
// Reuse permitted under the BSD license.
// https://www.shadertoy.com/view/4dsSzr
FfxFloat32x3 FFX_HeatmapGradient(FfxFloat32 t)
{
return clamp((pow(t, 1.5) * 0.8 + 0.2) * FfxFloat32x3(smoothstep(0.0, 0.35, t) + t * 0.5, smoothstep(0.5, 1.0, t), max(1.0 - t * 1.7, t * 7.0 - 6.0)), 0.0, 1.0);
}
// License?
FfxFloat32x3 FFX_RandomColor(FfxFloat32x2 uv) {
uv = ffxFract(uv * FfxFloat32(15.718281828459045));
FfxFloat32x3 seeds = FfxFloat32x3(FfxFloat32(0.123), FfxFloat32(0.456), FfxFloat32(0.789));
seeds = ffxFract((uv.x + FfxFloat32(0.5718281828459045) + seeds) * ((seeds + FFX_MODULO(uv.x, FfxFloat32(0.141592653589793))) * FfxFloat32(27.61803398875) + FfxFloat32(4.718281828459045)));
seeds = ffxFract((uv.y + FfxFloat32(0.5718281828459045) + seeds) * ((seeds + FFX_MODULO(uv.y, FfxFloat32(0.141592653589793))) * FfxFloat32(27.61803398875) + FfxFloat32(4.718281828459045)));
seeds = ffxFract((FfxFloat32(0.5718281828459045) + seeds) * ((seeds + FFX_MODULO(uv.x, FfxFloat32(0.141592653589793))) * FfxFloat32(27.61803398875) + FfxFloat32(4.718281828459045)));
return seeds;
}
FfxFloat32x3 FFX_ViewSpaceToWorldSpace(FfxFloat32x4 view_space_coord)
{
return FFX_TRANSFORM_VECTOR(GetDebugInfoInvView(), view_space_coord).xyz;
}
FfxFloat32x3 FFX_InvProjectPosition(FfxFloat32x3 coord, FfxFloat32x4x4 mat)
{
coord.y = (FfxFloat32(1.0) - coord.y);
coord.xy = FfxFloat32(2.0) * coord.xy - 1;
FfxFloat32x4 projected = FFX_TRANSFORM_VECTOR(mat, FfxFloat32x4(coord, 1));
projected.xyz /= projected.w;
return projected.xyz;
}
FfxFloat32x3 FFX_ScreenSpaceToViewSpace(FfxFloat32x3 screen_uv_coord)
{
return FFX_InvProjectPosition(screen_uv_coord, GetDebugInfoInvProj());
}
FfxFloat32 FFX_HitEdgeDist(FfxFloat32x3 hit, FfxFloat32x3 boxMin, FfxFloat32x3 boxMax)
{
FfxFloat32x3 a = min(abs(hit - boxMin), abs(hit - boxMax));
return min(min(a.x + a.y, a.x + a.z), a.y + a.z);
}
// modified from inigo quilez
FfxFloat32 FFX_BoxHitDist(FfxUInt32x2 tid, FfxFloat32x3 ro, FfxFloat32x3 rd, FfxFloat32x3 boxMin, FfxFloat32x3 boxMax)
{
FfxFloat32x3 halfSize = (boxMax - boxMin) / 2.0f;
FfxFloat32x3 center = boxMin + halfSize;
FfxFloat32x3 rop = ro - center;
FfxFloat32x3 ird = 1.0f / rd;
FfxFloat32x3 n = rop * ird;
FfxFloat32x3 k = abs(ird) * halfSize;
FfxFloat32x3 t1 = -n - k;
FfxFloat32x3 t2 = -n + k;
FfxFloat32 tNear = max(max(t1.x, t1.y), t1.z);
FfxFloat32 tFar = min(min(t2.x, t2.y), t2.z);
if (tNear > tFar || tFar < 0.0f) return FLT_INF;
FfxFloat32 nearEdgeDist = FFX_HitEdgeDist(ro + tNear * rd, boxMin, boxMax);
FfxFloat32 farEdgeDist = FFX_HitEdgeDist(ro + tFar * rd, boxMin, boxMax);
const FfxFloat32 nearClip = 0.1f;
if (tNear > nearClip && nearEdgeDist / tNear < 0.001f) {
return tNear;
} else if (tFar > nearClip && farEdgeDist / tFar < 0.001f) {
return tFar;
}
return FLT_INF;
}
void FfxBrixelizerDebugVisualization(FfxUInt32x2 tid)
{
FfxUInt32 width, height;
GetDebugOutputDimensions(width, height);
FfxFloat32x2 uv = (FfxFloat32x2(tid.xy) + FfxFloat32(0.5).xx) / FfxFloat32x2(width, height);
FfxFloat32x3 screen_uv_space_ray_origin = FfxFloat32x3(uv, FfxFloat32(0.5));
FfxFloat32x3 view_space_ray = FFX_ScreenSpaceToViewSpace(screen_uv_space_ray_origin);
FfxFloat32x3 view_space_ray_direction = normalize(view_space_ray);
FfxFloat32x3 ray_direction = normalize(FFX_ViewSpaceToWorldSpace(FfxFloat32x4(view_space_ray_direction, FfxFloat32(0.0))));
FfxFloat32x3 ray_origin = FFX_ViewSpaceToWorldSpace(FfxFloat32x4(0.0, 0.0, 0.0, 1.0));
FfxBrixelizerRayDesc ray_desc;
ray_desc.start_cascade_id = GetDebugInfoStartCascadeIndex();
ray_desc.end_cascade_id = GetDebugInfoEndCascadeIndex();
ray_desc.t_min = GetDebugInfoTMin();
ray_desc.t_max = GetDebugInfoTMax();
ray_desc.origin = ray_origin;
ray_desc.direction = ray_direction;
FfxBrixelizerHitRaw hit_payload;
FfxBoolean hit = FfxBrixelizerTraverseRaw(ray_desc, hit_payload);
FfxFloat32 hit_dist = FLT_INF;
FfxFloat32x3 out_color = FfxFloat32x3(0.0f, 0.0f, 0.0f);
if (hit) {
hit_dist = hit_payload.t;
switch (GetDebugInfoDebugState()) {
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_DISTANCE: {
FfxFloat32 dist = (hit_payload.t - ray_desc.t_min) / (ray_desc.t_max - ray_desc.t_min);
out_color = FfxFloat32x3(0.0f, smoothstep(0.0f, 1.0f, dist), smoothstep(0.0f, 1.0f, 1.0f - dist));
break;
}
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_UVW: {
FfxFloat32x3 uvw = FfxFloat32x3(
FfxBrixelizerUnpackUnsigned8Bits((hit_payload.uvwc >> 0) & 0xff),
FfxBrixelizerUnpackUnsigned8Bits((hit_payload.uvwc >> 8) & 0xff),
FfxBrixelizerUnpackUnsigned8Bits((hit_payload.uvwc >> 16) & 0xff)
);
out_color = uvw;
break;
}
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_ITERATIONS:
out_color = FFX_HeatmapGradient(FfxFloat32(hit_payload.iter_count) / FfxFloat32(64));
break;
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_GRAD: {
out_color = FfxBrixelizerGetHitNormal(hit_payload) * FfxFloat32(0.5) + FfxFloat32(0.5);
break;
}
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_BRICK_ID:
out_color = FFX_RandomColor(FfxFloat32x2(FfxFloat32(hit_payload.brick_id % 256) / FfxFloat32(256.0), FfxFloat32((hit_payload.brick_id / 256) % 256) / FfxFloat32(256.0)));
break;
case FFX_BRIXELIZER_TRACE_DEBUG_MODE_CASCADE_ID: {
FfxUInt32 voxel_id = LoadBricksVoxelMap(FfxBrixelizerBrickGetIndex(hit_payload.brick_id));
FfxUInt32 cascade_id = FfxBrixelizerGetVoxelCascade(voxel_id);
out_color = FFX_RandomColor(FfxFloat32x2(FfxFloat32(cascade_id % 256) / FfxFloat32(256.0), FfxFloat32((cascade_id / 256) % 256) / FfxFloat32(256.0)));
break;
}
}
} else {
out_color = FFX_HeatmapGradient(FfxFloat32(hit_payload.iter_count) / FfxFloat32(64));
}
FfxFloat32 aabb_hit_dist = FLT_INF;
FfxFloat32x3 aabb_color = FfxFloat32x3(0.0f, 0.0f, 0.0f);
FfxUInt32 num_debug_aabbs = min(GetDebugInfoMaxAABBs(), LoadContextCounter(FFX_BRIXELIZER_CONTEXT_COUNTER_NUM_DEBUG_AABBS));
for (FfxUInt32 i = 0; i < num_debug_aabbs; ++i) {
FfxBrixelizerDebugAABB aabb = GetDebugAABB(i);
FfxFloat32 this_hit_dist = FFX_BoxHitDist(tid, ray_desc.origin, ray_desc.direction, aabb.aabbMin, aabb.aabbMax);
if (this_hit_dist < aabb_hit_dist) {
aabb_hit_dist = this_hit_dist;
aabb_color = aabb.color;
}
}
if (aabb_hit_dist < hit_dist) {
out_color = aabb_color;
} else if (aabb_hit_dist < FLT_INF) {
out_color = ffxLerp(out_color, aabb_color, 0.25f);
}
StoreDebugOutput(tid, out_color);
}
#endif // ifndef FFX_BRIXELIZER_DEBUG_VISUALIZATION_H

View File

@@ -0,0 +1,230 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_HOST_GPU_SHARED_H
#define FFX_BRIXELIZER_HOST_GPU_SHARED_H
#include "../ffx_core.h"
#define FFX_BRIXELIZER_MAX_CASCADES 24
#define FFX_BRIXELIZER_MAX_INSTANCES (FfxUInt32(1) << FfxUInt32(16))
#define FFX_BRIXELIZER_CASCADE_RESOLUTION 64
#define FFX_BRIXELIZER_MAX_INSTANCES (FfxUInt32(1) << FfxUInt32(16))
#define FFX_BRIXELIZER_INVALID_ID 0x00ffffffu
#define FFX_BRIXELIZER_UNINITIALIZED_ID 0xffffffffu
#define FFX_BRIXELIZER_INVALID_BOTTOM_AABB_NODE 0x7fff
#define FFX_BRIXELIZER_MAX_BRICKS_X8 (1 << 18)
#define FFX_BRIXELIZER_MAX_BRICKS FFX_BRIXELIZER_MAX_BRICKS_X8
#define FFX_BRIXELIZER_BRICK_AABBS_STRIDE (sizeof(FfxUInt32))
#define FFX_BRIXELIZER_BRICK_AABBS_SIZE (FFX_BRIXELIZER_MAX_BRICKS_X8 * FFX_BRIXELIZER_BRICK_AABBS_STRIDE)
#define FFX_BRIXELIZER_CASCADE_AABB_TREE_SIZE ((16 * 16 * 16) * sizeof(FfxUInt32) + (4 * 4 * 4 + 1) * sizeof(FfxFloat32x3) * 2)
#define FFX_BRIXELIZER_CASCADE_AABB_TREE_STRIDE (sizeof(FfxUInt32))
#define FFX_BRIXELIZER_CASCADE_BRICK_MAP_SIZE (FFX_BRIXELIZER_CASCADE_RESOLUTION * FFX_BRIXELIZER_CASCADE_RESOLUTION * FFX_BRIXELIZER_CASCADE_RESOLUTION * sizeof(FfxUInt32))
#define FFX_BRIXELIZER_CASCADE_BRICK_MAP_STRIDE (sizeof(FfxUInt32))
#define FFX_BRIXELIZER_BRICK_ID_MASK 0xffffff
#define FFX_BRIXELIZER_CASCADE_DEGREE FfxUInt32(6)
#define FFX_BRIXELIZER_CASCADE_WRAP_MASK FfxUInt32(63)
#define FFX_BRIXELIZER_STATIC_CONFIG_SDF_ATLAS_SIZE FfxUInt32(512)
#if defined(FFX_CPU)
#define FFX_BRIXELIZER_CONST static const
#elif defined(FFX_HLSL)
#define FFX_BRIXELIZER_CONST static const
#elif defined(FFX_GLSL)
#define FFX_BRIXELIZER_CONST const
#else
FFX_STATIC_ASSERT(0)
#endif
#ifdef FFX_CPU
#define FFX_BRIXELIZER_BEGIN_STRUCT(name) typedef struct name {
#define FFX_BRIXELIZER_END_STRUCT(name) } name;
#define FFX_BRIXELIZER_BEGIN_ENUM(name) typedef enum name {
#define FFX_BRIXELIZER_END_ENUM(name) } name;
#define FFX_BRIXELIZER_ENUM_VALUE(name, value) name = value,
#else
#define FFX_BRIXELIZER_BEGIN_STRUCT(name) struct name {
#define FFX_BRIXELIZER_END_STRUCT(name) };
#define FFX_BRIXELIZER_BEGIN_ENUM(name)
#define FFX_BRIXELIZER_END_ENUM(name)
#define FFX_BRIXELIZER_ENUM_VALUE(name, value) FFX_BRIXELIZER_CONST FfxUInt32 name = value;
#endif
/// An enumeration of flags which can be specified for different options when doing a cascade update.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerCascadeUpdateFlags)
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_CASCADE_UPDATE_FLAG_NONE, FfxUInt32(0)) ///< No flags.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_CASCADE_UPDATE_FLAG_RESET, FfxUInt32(1) << FfxUInt32(0)) ///< Reset the cascade. This clears and frees all bricks currently in the cascade ready to rebuild the cascade completely.
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerCascadeUpdateFlags)
/// An enumeration of the different possible debug outputs for the Brixelizer debug visualization.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerTraceDebugModes)
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_DISTANCE, 0) ///< Display a visualisation of the distance to hit, with closer hits in blue and further hits in green.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_UVW, 1) ///< Display the UVW coordinates of hits.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_ITERATIONS, 2) ///< Display a heatmap visualizing number of iterations in the scene.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_GRAD, 3) ///< Display the normals at hits.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_BRICK_ID, 4) ///< Display each brick in its own color.
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_TRACE_DEBUG_MODE_CASCADE_ID, 5) ///< Display each cascade in itw own color.
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerTraceDebugModes)
/// A structure of parameters describing a cascade. This structure is primarily for Brixelizer
/// internal use.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerCascadeInfo)
FfxFloat32x3 grid_min;
FfxFloat32 voxel_size;
FfxFloat32x3 grid_max;
FfxUInt32 flags; ///< the latest build_flags; see FfxBrixelizerCascadeUpdateFlags
FfxUInt32x3 clipmap_offset;
FfxUInt32 pad00;
FfxInt32x3 clipmap_invalidation_offset;
FfxUInt32 pad33;
FfxInt32x3 ioffset;
FfxUInt32 index;
FfxFloat32x3 grid_mid;
FfxUInt32 is_enabled;
FfxUInt32x2 rel_grid_min_fp16;
FfxUInt32x2 rel_grid_max_fp16;
FfxUInt32 pad11;
FfxUInt32 pad22;
FfxFloat32 ivoxel_size;
FfxUInt32 is_initialized;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerCascadeInfo)
/// A structure of parameters describing the Brixelizer context. This structure is primarily
/// for Brixelizer internal use.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerContextInfo)
FfxUInt32 num_bricks;
FfxUInt32 frame_index;
FfxFloat32 imesh_unit;
FfxFloat32 mesh_unit;
FfxBrixelizerCascadeInfo cascades[FFX_BRIXELIZER_MAX_CASCADES];
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerContextInfo)
// =============================================================================
// Debug/context counters
// =============================================================================
// Important to match the first a couple of counters with the FfxBrixelizerScratchCounters to share code
#define MEMBER_LIST \
MEMBER(brickCount, BRICK_COUNT, 0) \
MEMBER(dirtyBricks, DIRTY_BRICKS, 1) \
MEMBER(freeBricks, FREE_BRICKS, 2) \
MEMBER(clearBricks, CLEAR_BRICKS, 3) \
MEMBER(mergeBricks, MERGE_BRICKS, 4) \
MEMBER(numDebugAABBs, NUM_DEBUG_AABBS, 5)
/// A structure containing all the counters used by the Brixelizer context. These can
/// be read back from the context after processing each update for analysis.
/// <c><i>brickCount</i></c> gives the total number of bricks allocated.
/// <c><i>dirtyBricks</i></c> gives the total number of bricks requiring an eikonal pass for completion.
/// <c><i>freeBricks</i></c> gives the total number of free bricks. This is the maximum number of bricks which can be allocated within a frame.
/// <c><i>clearBricks</i></c> gives the total number of bricks to be cleared in a frame. Bricks are cleared by having all distance values reset to 1.
/// <c><i>mergeBricks</i></c> gives the total number of bricks to be merged in a frame.
/// <c><i>numDebugAABBs</i></c> gives the total number of debug AABBs requested to be drawn in a debug visualization.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerDebugCounters)
#define MEMBER(camel_name, _upper_name, _index) FfxUInt32 camel_name;
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerDebugCounters)
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerContextCounterIndex)
#define MEMBER(_camel_name, upper_name, index) FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_CONTEXT_COUNTER_##upper_name, index)
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerContextCounterIndex)
#define MEMBER(_camel_name, _upper_name, _index) + 1
FFX_BRIXELIZER_CONST FfxUInt32 FFX_BRIXELIZER_NUM_CONTEXT_COUNTERS = 0 MEMBER_LIST;
#undef MEMBER
#undef MEMBER_LIST
// =============================================================================
// Scratch counters
// =============================================================================
#define MEMBER_LIST \
MEMBER(triangles, TRIANGLES, 0) \
MEMBER(maxTriangles, MAX_TRIANGLES, 1) \
MEMBER(references, REFERENCES, 2) \
MEMBER(maxReferences, MAX_REFERENCES, 3) \
MEMBER(groupIndex, GROUP_INDEX, 4) \
MEMBER(compressionBricks, COMPRESSION_BRICKS, 5) \
MEMBER(storageOffset, STORAGE_OFFSET, 6) \
MEMBER(storageSize, STORAGE_SIZE, 7) \
MEMBER(numBricksAllocated, NUM_BRICKS_ALLOCATED, 8) \
MEMBER(clearBricks, CLEAR_BRICKS, 9)
/// A structure containing the counters used by the Brixelizer context for each cascade
/// update. This can be readback and used for analysis after each update. The following
/// members contain useful information for analysing Brixelizer resource usage.
///
/// <c><i>triangles</i></c> is used to store the total amount of storage space requested within the triangle buffer during an update. This is useful for determining a sensible value of <c><i>triangleSwapSize</i></c> in either <c><i>FfxBrixelizerRawCascadeUpdateDescription</i></c> or <c><i>FfxBrixelizerUpdateDescription</i></c>.
/// <c><i>references</i></c> is used to store the total number of reference allocations requested by Brixelizer during an update. This is useful for determining a sensible value of <c><i>maxReferences</i></c> in either <c><i>FfxBrixelizerRawCascadeUpdateDescription</i></c> or <c><i>FfxBrixelizerUpdateDescription</i></c>.
/// <c><i>numBricksAllocated</i></c> is used to store the number of brick allocations requested in an update. This is useful for determining a sensible value of <c><i>maxBricksPerBake</i></c> in either <c><i>FfxBrixelizerRawCascadeUpdateDescription</i></c> or <c><i>FfxBrixelizerUpdateDescription</i></c>.
///
/// The following counters are used internally by Brixelizer.
///
/// <c><i>maxTriangles</i></c> is used to store the storage size of the triangle buffer.
/// <c><i>maxReferences</i></c> is used to store the maxmimum number of references that can be stored.
/// <c><i>groupIndex</i></c> is used as a global atomic for wavefront synchronisation.
/// <c><i>compressionBricks</i></c> the number of bricks to compress (i.e. calculate AABBs for) this udpate.
/// <c><i>storageOffset</i></c> the next free position in the bricks scratch buffer.
/// <c><i>storageSize</i></c> the size of the bricks scratch buffer.
/// <c><i>clearBricks</i></c> the amount of bricks in the scratch buffer to initialize.
///
/// @ingroup Brixelizer
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerScratchCounters)
#define MEMBER(camel_name, _upper_name, _index) FfxUInt32 camel_name;
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerScratchCounters)
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerScratchCounterIndex)
#define MEMBER(_camel_name, upper_name, index) FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_SCRATCH_COUNTER_##upper_name, index)
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerScratchCounterIndex)
#define MEMBER(_camel_name, _upper_name, _index) + 1
FFX_BRIXELIZER_CONST FfxUInt32 FFX_BRIXELIZER_NUM_SCRATCH_COUNTERS = 0 MEMBER_LIST;
#undef MEMBER
#undef MEMBER_LIST
#endif

View File

@@ -0,0 +1,222 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_HOST_GPU_SHARED_PRIVATE_H
#define FFX_BRIXELIZER_HOST_GPU_SHARED_PRIVATE_H
#include "../ffx_core.h"
#include "ffx_brixelizer_host_gpu_shared.h"
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerInstanceFlagsInternal)
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_USE_U16_INDEX, (FfxUInt32(1) << FfxUInt32(0)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_USE_RGBA16_VERTEX, (FfxUInt32(1) << FfxUInt32(2)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_USE_INDEXLESS_QUAD_LIST, (FfxUInt32(1) << FfxUInt32(3)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_UV_FORMAT_RG16_UNORM, (FfxUInt32(1) << FfxUInt32(4)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_UV_FORMAT_RG32_FLOAT, (FfxUInt32(1) << FfxUInt32(5)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_COLOR_FORMAT_RGBA32_FLOAT, (FfxUInt32(1) << FfxUInt32(6)))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INSTANCE_FLAG_COLOR_FORMAT_RGBA8_UNORM, (FfxUInt32(1) << FfxUInt32(7)))
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerInstanceFlagsInternal)
#define FFX_BRIXELIZER_STATIC_CONFIG_VOXELIZER_GROUP_SIZE FfxUInt32(256)
#define FFX_BRIXELIZER_STATIC_CONFIG_SCAN_REFERENCES_GROUP_SIZE FfxUInt32(256)
#define FFX_BRIXELIZER_STATIC_CONFIG_MAX_VERTEX_BUFFERS FfxUInt32(8192)
#define FFX_BRIXELIZER_STATIC_CONFIG_COMPACT_REFERENCES_GROUP_SIZE FfxUInt32(64)
#define FFX_BRIXELIZER_STATIC_CONFIG_EMIT_SDF_GROUP_SIZE FfxUInt32(32)
#define FFX_BRIXELIZER_STATIC_CONFIG_EMIT_SDF_REFS_PER_GROUP FFX_BRIXELIZER_STATIC_CONFIG_EMIT_SDF_GROUP_SIZE
#define FFX_BRIXELIZER_STATIC_CONFIG_INDIRECT_DISPATCH_STRIDE32 FfxUInt32(4)
#define FFX_BRIXELIZER_STATIC_CONFIG_INDIRECT_DISPATCH_STRIDE FfxUInt32(16)
// index deliberately starts at 1
#define MEMBER_LIST \
MEMBER(CompactReferences, COMPACT_REFERENCES, 1) \
MEMBER(EmitSDF, EMIT_SDF, 2) \
MEMBER(Voxelize, VOXELIZE, 3) \
MEMBER(Compress, COMPRESS, 4) \
MEMBER(Eikonal, EIKONAL, 5) \
MEMBER(ClearBricks, CLEAR_BRICKS, 6) \
MEMBER(MergeBricks, MERGE_BRICKS, 7) \
MEMBER(InstanceOpsTrianglePass1, INSTANCE_OPS_TRIANGLE_PASS_1, 8) \
MEMBER(InstanceOpsDeferredReleaseBrits, INSTANCE_OPS_DEFERRED_RELEASE_BRITS, 9) \
MEMBER(ClearBrits, CLEAR_BRITS, 10)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerIndirectOffsets)
FfxUInt32x4 pad;
#define MEMBER(pascal_name, _upper_name, _index) FfxUInt32x4 pascal_name;
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerIndirectOffsets)
#define MEMBER(_pascal_name, _upper_name, _index) + 1
FFX_BRIXELIZER_CONST FfxUInt32 FFX_BRIXELIZER_NUM_INDIRECT_OFFSETS = 1 MEMBER_LIST;
#undef MEMBER
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerIndirectOffsetsEnum)
#define MEMBER(_pascal_name, upper_name, index) FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INDIRECT_OFFSETS_##upper_name, index * FFX_BRIXELIZER_STATIC_CONFIG_INDIRECT_DISPATCH_STRIDE)
MEMBER_LIST
#undef MEMBER
#define MEMBER(_pascal_name, upper_name, index) FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_INDIRECT_OFFSETS_##upper_name##_32, index * FFX_BRIXELIZER_STATIC_CONFIG_INDIRECT_DISPATCH_STRIDE32)
MEMBER_LIST
#undef MEMBER
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerIndirectOffsetsEnum)
#undef MEMBER_LIST
FFX_BRIXELIZER_BEGIN_ENUM(FfxBrixelizerJobFlagsInternal)
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_JOB_FLAG_NONE, FfxUInt32(0))
FFX_BRIXELIZER_ENUM_VALUE(FFX_BRIXELIZER_JOB_FLAG_INVALIDATE, (FfxUInt32(1) << FfxUInt32(0)))
FFX_BRIXELIZER_END_ENUM(FfxBrixelizerJobFlagsInternal)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerBasicMeshInfo)
FfxUInt32 flags;
FfxUInt32 indexBufferID;
FfxUInt32 indexBufferOffset;
FfxUInt32 vertexBufferID;
FfxUInt32 vertexBufferOffset;
FfxUInt32 vertexCount;
FfxUInt32 vertexStride;
FfxUInt32 triangleCount;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerBasicMeshInfo)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerInstanceInfo)
FfxFloat32x3 aabbMin; // voxel aligned
FfxUInt32 pack0; // | 16: vertex buffer id | 16: index buffer id |
FfxFloat32x3 aabbMax; // voxel aligned
FfxUInt32 pack1; // ! 6: vertex stride | 10: flags | | |
FfxUInt32 indexBufferOffset;
FfxUInt32 vertexBufferOffset;
FfxUInt32 vertexCount;
FfxUInt32 triangleCount;
FfxUInt32x3 padding;
FfxUInt32 index;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerInstanceInfo)
#ifdef FFX_GPU
FfxBrixelizerBasicMeshInfo ffxBrixelizerInstanceInfoGetMeshInfo(FFX_PARAMETER_IN FfxBrixelizerInstanceInfo instance_info)
{
FfxBrixelizerBasicMeshInfo info;
info.flags = (instance_info.pack1 >> 16) & FfxUInt32(0x1ff);
info.triangleCount = instance_info.triangleCount;
info.indexBufferID = (instance_info.pack0 >> 0) & FfxUInt32(0xffff);
info.indexBufferOffset = instance_info.indexBufferOffset;
info.vertexBufferID = (instance_info.pack0 >> 16) & FfxUInt32(0xffff);
info.vertexBufferOffset = instance_info.vertexBufferOffset;
info.vertexStride = (instance_info.pack1 >> 26) & 0x3f;
return info;
}
#endif
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerBrixelizationJob)
FfxUInt32x3 aabbMin; // in local cascade voxel size
FfxUInt32 instanceIdx;
FfxUInt32x3 aabbMax; // in local cascade voxel size
FfxUInt32 flags;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerBrixelizationJob)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerIndexRef)
FfxUInt32 surface_id;
FfxUInt32 instance_id;
FfxUInt32 triangle_index;
FfxUInt32 pad0;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerIndexRef)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerTriangleReference)
FfxUInt32 voxel_idx;
FfxUInt32 triangle_id;
FfxUInt32 local_ref_idx;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerTriangleReference)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerInstanceReference)
FfxUInt32 voxel_idx;
FfxUInt32 instance_id;
FfxUInt32 local_ref_idx;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerInstanceReference)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerTriangle)
FfxUInt32 job_idx;
FfxUInt32 triangle_index;
FfxUInt32x3 face3;
FfxFloat32x3 wp0;
FfxFloat32x3 wp1;
FfxFloat32x3 wp2;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerTriangle)
#define FFX_BRIXELIZER_TRIANGLE_SMALL_FLAG (FfxUInt32(1) << FfxUInt32(31))
#define FFX_BRIXELIZER_TRIANGLE_OFFSET_MASK (0x7fffffffu)
#define FFX_BRIXELIZER_INVALID_ALLOCATION 0x00ffffffu
#define FFX_BRIXELIZER_VOXEL_ID_MASK 0xffffff
#define FFX_BRIXELIZER_CASCADE_ID_SHIFT 24
#ifdef FFX_GPU
FfxBoolean FfxBrixelizerTriangleIsSmall(FfxUInt32 id) { return ((id & FFX_BRIXELIZER_TRIANGLE_SMALL_FLAG) != 0); }
FfxUInt32 FfxBrixelizerTriangleIDGetOffset(FfxUInt32 id) { return id & FFX_BRIXELIZER_TRIANGLE_OFFSET_MASK; }
FfxBoolean FfxBrixelizerIsInvalidID(FfxUInt32 id) { return (id & FFX_BRIXELIZER_INVALID_ID) == FFX_BRIXELIZER_INVALID_ID; }
#endif
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerBuildInfo)
FfxInt32 tree_iteration;
FfxUInt32 max_bricks_per_bake;
FfxUInt32 num_jobs;
FfxUInt32 cascade_index;
FfxUInt32 cascade_shift; // [1, 2, 4] for 1x, 2x and 4x
FfxUInt32 is_dynamic;
FfxUInt32 do_initialization;
FfxUInt32 num_job_voxels;
FfxUInt32 src_cascade_A;
FfxUInt32 src_cascade_B;
FfxUInt32 dst_cascade;
FfxUInt32 merge_instance_idx;
FfxUInt32x3 _padding;
FfxUInt32 build_flags;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerBuildInfo)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerDebugAABB)
FfxFloat32x3 color;
FfxFloat32x3 aabbMin;
FfxFloat32x3 aabbMax;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerDebugAABB)
FFX_BRIXELIZER_BEGIN_STRUCT(FfxBrixelizerDebugInfo)
FfxFloat32x4x4 inv_view; ///< Stored in row-major order.
FfxFloat32x4x4 inv_proj; ///< Stored in row-major order.
FfxFloat32 t_min;
FfxFloat32 t_max;
FfxFloat32 preview_sdf_solve_eps;
FfxUInt32 start_cascade_idx;
FfxUInt32 end_cascade_idx;
FfxUInt32 debug_state;
FfxUInt32 max_aabbs;
FfxUInt32 _padding;
FFX_BRIXELIZER_END_STRUCT(FfxBrixelizerDebugInfo)
#endif // FFX_BRIXELIZER_HOST_GPU_SHARED_PRIVATE_H

View File

@@ -0,0 +1,173 @@
// This file is part of the FidelityFX SDK.
//
// Copyright (C) 2024 Advanced Micro Devices, Inc.
//
// 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.
#ifndef FFX_BRIXELIZER_MESH_COMMON_H
#define FFX_BRIXELIZER_MESH_COMMON_H
#include "ffx_brixelizer_common_private.h"
struct FfxBrixelizerTrianglePos {
FfxUInt32x3 face3;
FfxFloat32x3 wp0;
FfxFloat32x3 wp1;
FfxFloat32x3 wp2;
};
FfxFloat32x3 FFX_Fetch_PositionRGBA16(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
FfxUInt32x2 pack = LoadVertexBufferUInt2(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
FfxFloat32x2 xy = ffxUnpackF32(pack.x);
FfxFloat32x2 zw = ffxUnpackF32(pack.y);
#if 0
FfxFloat32 x = f16tof32((pack.x >> FfxUInt32(0)) & FfxUInt32(0xFFFF));
FfxFloat32 y = f16tof32((pack.x >> FfxUInt32(16)) & FfxUInt32(0xFFFF));
FfxFloat32 z = f16tof32((pack.y >> FfxUInt32(0)) & FfxUInt32(0xFFFF));
FfxFloat32 w = f16tof32((pack.y >> FfxUInt32(16)) & FfxUInt32(0xFFFF));
#endif
return FfxFloat32x4(xy, zw).xyz;
}
FfxFloat32x4 FFX_Fetch_Unorm4(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
FfxUInt32 pack = LoadVertexBufferUInt(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
FfxFloat32 x = FfxFloat32((pack >> FfxUInt32(0)) & FfxUInt32(0xFF)) / FfxFloat32(255.0);
FfxFloat32 y = FfxFloat32((pack >> FfxUInt32(8)) & FfxUInt32(0xFF)) / FfxFloat32(255.0);
FfxFloat32 z = FfxFloat32((pack >> FfxUInt32(16)) & FfxUInt32(0xFF)) / FfxFloat32(255.0);
FfxFloat32 w = FfxFloat32((pack >> FfxUInt32(24)) & FfxUInt32(0xFF)) / FfxFloat32(255.0);
return FfxFloat32x4(x, y, z, w);
}
FfxFloat32x2 FFX_Fetch_RG16_UNORM(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
FfxUInt32 pack = LoadVertexBufferUInt(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
FfxFloat32 x = FfxFloat32((pack >> FfxUInt32(0)) & 0xFFFFu) / FfxFloat32(65535.0);
FfxFloat32 y = FfxFloat32((pack >> FfxUInt32(16)) & 0xFFFFu) / FfxFloat32(65535.0);
return FfxFloat32x2(x, y);
}
void FFX_Fetch_Face_Indices_U32(FfxUInt32 buffer_id, FFX_PARAMETER_OUT FfxUInt32x3 face3, FfxUInt32 offset, FfxUInt32 triangle_id)
{
face3 = LoadVertexBufferUInt3(buffer_id, (offset + FfxUInt32(12) * triangle_id) / FFX_BRIXELIZER_SIZEOF_UINT);
}
void FFX_Fetch_Face_Indices_U16(FfxUInt32 buffer_id, FFX_PARAMETER_OUT FfxUInt32x3 face3, FfxUInt32 offset, FfxUInt32 triangle_id)
{
FfxUInt32 word_offset = offset / 2;
FfxUInt32 word_id_0 = word_offset + triangle_id * FfxUInt32(3) + FfxUInt32(0);
FfxUInt32 dword_id_0 = word_id_0 / FfxUInt32(2);
FfxUInt32 shift_0 = FfxUInt32(16) * (word_id_0 & FfxUInt32(1));
FfxUInt32 word_id_1 = word_offset + triangle_id * FfxUInt32(3) + FfxUInt32(1);
FfxUInt32 dword_id_1 = word_id_1 / FfxUInt32(2);
FfxUInt32 shift_1 = FfxUInt32(16) * (word_id_1 & FfxUInt32(1));
FfxUInt32 word_id_2 = word_offset + triangle_id * FfxUInt32(3) + FfxUInt32(2);
FfxUInt32 dword_id_2 = word_id_2 / FfxUInt32(2);
FfxUInt32 shift_2 = FfxUInt32(16) * (word_id_2 & FfxUInt32(1));
FfxUInt32 u0 = LoadVertexBufferUInt(buffer_id, dword_id_0);
u0 = (u0 >> shift_0) & FfxUInt32(0xFFFF);
FfxUInt32 u1 = LoadVertexBufferUInt(buffer_id, dword_id_1);
u1 = (u1 >> shift_1) & FfxUInt32(0xFFFF);
FfxUInt32 u2 = LoadVertexBufferUInt(buffer_id, dword_id_2);
u2 = (u2 >> shift_2) & FfxUInt32(0xFFFF);
face3 = FfxUInt32x3(u0, u1, u2);
}
FfxFloat32x2 FFX_Fetch_float2(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
return LoadVertexBufferFloat2(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
}
FfxFloat32x3 FFX_Fetch_float3(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
return LoadVertexBufferFloat3(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
}
FfxFloat32x4 FFX_Fetch_float4(FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
return LoadVertexBufferFloat4(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
}
FfxFloat32x3 FFX_Fetch_Position(FfxUInt32 flags, FfxUInt32 buffer_id, FfxUInt32 offset, FfxUInt32 vertex_id, FfxUInt32 stride)
{
if (FFX_HAS_FLAG(flags, FFX_BRIXELIZER_INSTANCE_FLAG_USE_RGBA16_VERTEX)) {
FfxUInt32x2 pack = LoadVertexBufferUInt2(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
FfxFloat32x2 xy = ffxUnpackF32(pack.x);
FfxFloat32x2 zw = ffxUnpackF32(pack.y);
return FfxFloat32x4(xy, zw).xyz;
} else
return LoadVertexBufferFloat3(buffer_id, (offset + stride * vertex_id) / FFX_BRIXELIZER_SIZEOF_UINT);
}
FfxUInt32x3 FfxBrixelizerFetchFace(FfxUInt32 flags, FfxUInt32 indexBufferID, FfxUInt32 indexBufferOffset, FfxUInt32 triangle_idx)
{
if (FFX_HAS_FLAG(flags, FFX_BRIXELIZER_INSTANCE_FLAG_USE_INDEXLESS_QUAD_LIST)) { // Procedural quad index buffer
FfxUInt32 quad_id = triangle_idx / FfxUInt32(2); // 2 triangles per quad
FfxUInt32 base_vertex = quad_id * FfxUInt32(4); // 4 vertices per quad
if (FFX_HAS_FLAG(triangle_idx, FfxUInt32(1))) {
return FFX_BROADCAST_UINT32X3(base_vertex) + FfxUInt32x3(FfxUInt32(2), FfxUInt32(3), FfxUInt32(0));
} else {
return FFX_BROADCAST_UINT32X3(base_vertex) + FfxUInt32x3(FfxUInt32(0), FfxUInt32(1), FfxUInt32(2));
}
} else {
FfxUInt32x3 face3;
if (FFX_HAS_FLAG(flags, FFX_BRIXELIZER_INSTANCE_FLAG_USE_U16_INDEX)) {
FFX_Fetch_Face_Indices_U16(indexBufferID, /* out */ face3, /* in */ indexBufferOffset, /* in */ triangle_idx);
} else { // FFX_BRIXELIZER_INDEX_TYPE_U32
FFX_Fetch_Face_Indices_U32(indexBufferID, /* out */ face3, /* in */ indexBufferOffset, /* in */ triangle_idx);
}
return face3;
}
}
FfxBrixelizerTrianglePos FfxBrixelizerFetchTriangle(FfxUInt32 flags, FfxUInt32 indexBufferID, FfxUInt32 indexBufferOffset, FfxUInt32 vertexBufferID, FfxUInt32 vertexBufferOffset, FfxUInt32 vertexBufferStride,
FfxUInt32 instance_idx, FfxUInt32 triangle_idx)
{
FfxUInt32x3 face3 = FfxBrixelizerFetchFace(flags, indexBufferID, indexBufferOffset, triangle_idx);
FfxFloat32x3 p0 = FFX_Fetch_Position(flags, vertexBufferID, vertexBufferOffset, face3.x, vertexBufferStride);
FfxFloat32x3 p1 = FFX_Fetch_Position(flags, vertexBufferID, vertexBufferOffset, face3.y, vertexBufferStride);
FfxFloat32x3 p2 = FFX_Fetch_Position(flags, vertexBufferID, vertexBufferOffset, face3.z, vertexBufferStride);
FfxBrixelizerTrianglePos tri;
FfxFloat32x3x4 obj_to_anchor = LoadInstanceTransform(instance_idx);
tri.face3 = face3;
tri.wp0 = FFX_TRANSFORM_VECTOR(obj_to_anchor, FfxFloat32x4(p0, FfxFloat32(1.0))).xyz;
tri.wp1 = FFX_TRANSFORM_VECTOR(obj_to_anchor, FfxFloat32x4(p1, FfxFloat32(1.0))).xyz;
tri.wp2 = FFX_TRANSFORM_VECTOR(obj_to_anchor, FfxFloat32x4(p2, FfxFloat32(1.0))).xyz;
return tri;
}
FfxBrixelizerTrianglePos FfxBrixelizerFetchTriangle(FfxBrixelizerBasicMeshInfo mesh_info, FfxUInt32 instance_id, FfxUInt32 triangle_id)
{
return FfxBrixelizerFetchTriangle(mesh_info.flags, mesh_info.indexBufferID, mesh_info.indexBufferOffset, mesh_info.vertexBufferID, mesh_info.vertexBufferOffset,
mesh_info.vertexStride, instance_id, triangle_id);
}
#endif // FFX_BRIXELIZER_MESH_COMMON_H

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