diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index 1aa648070..0d2867769 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -150,6 +150,8 @@ You can use the Renderer with the following functions, all of which are in the g - SetDebugCamerasEnabled(bool value) - SetVSyncEnabled(opt bool enabled) - SetOcclusionCullingEnabled(bool enabled) +- SetMeshShaderAllowed(bool enabled) +- SetMeshletOcclusionCullingEnabled(bool value) - DrawLine(Vector origin,end, opt Vector color, opt bool depth = false) - DrawPoint(Vector origin, opt float size, opt Vector color, opt bool depth = false) - DrawBox(Matrix boxMatrix, opt Vector color, opt bool depth = true) diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index 268f67ca0..50931fe80 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -73,6 +73,7 @@ void Editor::Initialize() infoDisplay.active = true; infoDisplay.watermark = false; // can be toggled instead on gui + infoDisplay.pipeline_creation = true; //infoDisplay.fpsinfo = true; //infoDisplay.resolution = true; //infoDisplay.logical_size = true; diff --git a/Editor/GraphicsWindow.cpp b/Editor/GraphicsWindow.cpp index 63192a91c..985a3bf96 100644 --- a/Editor/GraphicsWindow.cpp +++ b/Editor/GraphicsWindow.cpp @@ -137,6 +137,38 @@ void GraphicsWindow::Create(EditorComponent* _editor) }); AddWidget(&visibilityComputeShadingCheckBox); + meshShaderCheckBox.Create("Allow Mesh Shader: "); + meshShaderCheckBox.SetTooltip("Allow using mesh shaders to render objects (Requires support from GPU)."); + meshShaderCheckBox.SetPos(XMFLOAT2(x, y += step)); + meshShaderCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + if (editor->main->config.GetSection("graphics").Has("mesh_shader")) + { + wi::renderer::SetMeshShaderAllowed(editor->main->config.GetSection("graphics").GetBool("mesh_shader")); + } + meshShaderCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::renderer::SetMeshShaderAllowed(args.bValue); + editor->main->config.GetSection("graphics").Set("mesh_shader", args.bValue); + editor->main->config.Commit(); + }); + AddWidget(&meshShaderCheckBox); + meshShaderCheckBox.SetEnabled(wi::graphics::GetDevice()->CheckCapability(wi::graphics::GraphicsDeviceCapability::MESH_SHADER)); + + meshletOcclusionCullingCheckBox.Create("Meshlet Occlusion Culling: "); + meshletOcclusionCullingCheckBox.SetTooltip("Whether individual meshlets can use specialized occlusion culling (requires GPU support for mesh shaders)."); + meshletOcclusionCullingCheckBox.SetPos(XMFLOAT2(x, y += step)); + meshletOcclusionCullingCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + if (editor->main->config.GetSection("graphics").Has("meshlet_occlusion_culling")) + { + wi::renderer::SetMeshletOcclusionCullingEnabled(editor->main->config.GetSection("graphics").GetBool("meshlet_occlusion_culling")); + } + meshletOcclusionCullingCheckBox.OnClick([=](wi::gui::EventArgs args) { + wi::renderer::SetMeshletOcclusionCullingEnabled(args.bValue); + editor->main->config.GetSection("graphics").Set("meshlet_occlusion_culling", args.bValue); + editor->main->config.Commit(); + }); + AddWidget(&meshletOcclusionCullingCheckBox); + meshletOcclusionCullingCheckBox.SetEnabled(wi::graphics::GetDevice()->CheckCapability(wi::graphics::GraphicsDeviceCapability::MESH_SHADER)); + GIBoostSlider.Create(1, 10, 1.0f, 1000.0f, "GI Boost: "); GIBoostSlider.SetTooltip("Adjust the strength of GI.\nNote that values other than 1.0 will cause mismatch with path tracing reference!"); GIBoostSlider.SetSize(XMFLOAT2(wid, itemheight)); @@ -1506,6 +1538,8 @@ void GraphicsWindow::Update() occlusionCullingCheckBox.SetCheck(wi::renderer::GetOcclusionCullingEnabled()); GIBoostSlider.SetValue(wi::renderer::GetGIBoost()); visibilityComputeShadingCheckBox.SetCheck(editor->renderPath->visibility_shading_in_compute); + meshShaderCheckBox.SetCheck(wi::renderer::IsMeshShaderAllowed()); + meshletOcclusionCullingCheckBox.SetCheck(wi::renderer::IsMeshletOcclusionCullingEnabled()); resolutionScaleSlider.SetValue(editor->resolutionScale); streamingSlider.SetValue(wi::resourcemanager::GetStreamingMemoryThreshold()); MSAAComboBox.SetSelectedByUserdataWithoutCallback(editor->renderPath->getMSAASampleCount()); @@ -1668,6 +1702,8 @@ void GraphicsWindow::ResizeLayout() advancedLightCullingCheckBox.SetVisible(false); occlusionCullingCheckBox.SetVisible(false); visibilityComputeShadingCheckBox.SetVisible(false); + meshShaderCheckBox.SetVisible(false); + meshletOcclusionCullingCheckBox.SetVisible(false); tessellationCheckBox.SetVisible(false); } else @@ -1684,6 +1720,8 @@ void GraphicsWindow::ResizeLayout() advancedLightCullingCheckBox.SetVisible(true); occlusionCullingCheckBox.SetVisible(true); visibilityComputeShadingCheckBox.SetVisible(true); + meshShaderCheckBox.SetVisible(true); + meshletOcclusionCullingCheckBox.SetVisible(true); tessellationCheckBox.SetVisible(true); add(shadowTypeComboBox); @@ -1698,6 +1736,8 @@ void GraphicsWindow::ResizeLayout() advancedLightCullingCheckBox.SetPos(XMFLOAT2(debugLightCullingCheckBox.GetPos().x - advancedLightCullingCheckBox.GetSize().x - 70, debugLightCullingCheckBox.GetPos().y)); add_right(occlusionCullingCheckBox); add_right(visibilityComputeShadingCheckBox); + add_right(meshShaderCheckBox); + add_right(meshletOcclusionCullingCheckBox); add_right(tessellationCheckBox); } diff --git a/Editor/GraphicsWindow.h b/Editor/GraphicsWindow.h index a23d9af94..b64a90fd9 100644 --- a/Editor/GraphicsWindow.h +++ b/Editor/GraphicsWindow.h @@ -15,6 +15,8 @@ public: wi::gui::Label pathTraceStatisticsLabel; wi::gui::CheckBox occlusionCullingCheckBox; wi::gui::CheckBox visibilityComputeShadingCheckBox; + wi::gui::CheckBox meshShaderCheckBox; + wi::gui::CheckBox meshletOcclusionCullingCheckBox; wi::gui::Slider resolutionScaleSlider; wi::gui::Slider streamingSlider; wi::gui::Slider GIBoostSlider; diff --git a/Editor/MeshWindow.cpp b/Editor/MeshWindow.cpp index 1e5ced7e8..749b0cbf6 100644 --- a/Editor/MeshWindow.cpp +++ b/Editor/MeshWindow.cpp @@ -76,17 +76,7 @@ void MeshWindow::Create(EditorComponent* _editor) archive << EditorComponent::HISTORYOP_COMPONENT_DATA; editor->RecordEntity(archive, entity); - uint32_t first_subset = 0; - uint32_t last_subset = 0; - mesh->GetLODSubsetRange(0, first_subset, last_subset); - wi::vector newSubsets; - for (uint32_t i = first_subset; i < last_subset; ++i) - { - newSubsets.push_back(mesh->subsets[i]); - } - newSubsets.emplace_back().indexCount = (uint32_t)mesh->indices.size(); - mesh->subsets = newSubsets; - mesh->subsets_per_lod = 0; + mesh->CreateSubset(); editor->RecordEntity(archive, entity); } @@ -969,6 +959,10 @@ void MeshWindow::SetEntity(Entity entity, int subset) { ss += "Morph target count: " + std::to_string(mesh->morph_targets.size()) + "\n"; } + if (!mesh->cluster_ranges.empty()) + { + ss += "Cluster count: " + std::to_string(mesh->GetClusterCount()) + "\n"; + } ss += "CPU memory: " + wi::helper::GetMemorySizeText(mesh->GetMemoryUsageCPU()) + "\n"; if (mesh->bvh.IsValid()) { diff --git a/WickedEngine/offlineshadercompiler.cpp b/WickedEngine/offlineshadercompiler.cpp index 8724aa686..acbcbdf9b 100644 --- a/WickedEngine/offlineshadercompiler.cpp +++ b/WickedEngine/offlineshadercompiler.cpp @@ -231,6 +231,8 @@ wi::vector shaders = { {"yuv_to_rgbCS", wi::graphics::ShaderStage::CS }, {"wetmap_updateCS", wi::graphics::ShaderStage::CS }, {"causticsCS", wi::graphics::ShaderStage::CS }, + {"depth_reprojectCS", wi::graphics::ShaderStage::CS }, + {"depth_pyramidCS", wi::graphics::ShaderStage::CS }, {"emittedparticlePS_soft", wi::graphics::ShaderStage::PS }, @@ -361,6 +363,16 @@ wi::vector shaders = { {"emittedparticleMS", wi::graphics::ShaderStage::MS }, + {"objectMS", wi::graphics::ShaderStage::MS }, + {"objectMS_prepass", wi::graphics::ShaderStage::MS }, + {"objectMS_prepass_alphatest", wi::graphics::ShaderStage::MS }, + {"objectMS_simple", wi::graphics::ShaderStage::MS }, + {"shadowMS", wi::graphics::ShaderStage::MS }, + {"shadowMS_alphatest", wi::graphics::ShaderStage::MS }, + {"shadowMS_transparent", wi::graphics::ShaderStage::MS }, + + {"objectAS", wi::graphics::ShaderStage::AS }, + //{"rtreflectionLIB", wi::graphics::ShaderStage::LIB }, }; diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index 0575fbf28..6f02e812b 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -485,8 +485,9 @@ struct alignas(16) ShaderGeometry uint indexOffset; uint indexCount; - int padding0; - int padding1; + int vb_clu; + int vb_bou; + void init() { @@ -498,6 +499,8 @@ struct alignas(16) ShaderGeometry vb_col = -1; vb_atl = -1; vb_pre = -1; + vb_clu = -1; + vb_bou = -1; materialIndex = 0; meshletOffset = 0; meshletCount = 0; @@ -516,8 +519,8 @@ struct alignas(16) ShaderGeometry } }; -// 256 triangle batch of a ShaderGeometry -static const uint MESHLET_TRIANGLE_COUNT = 256u; +static const uint MESHLET_VERTEX_COUNT = 64u; +static const uint MESHLET_TRIANGLE_COUNT = 124u; inline uint triangle_count_to_meshlet_count(uint triangleCount) { return (triangleCount + MESHLET_TRIANGLE_COUNT - 1u) / MESHLET_TRIANGLE_COUNT; @@ -526,10 +529,52 @@ struct alignas(16) ShaderMeshlet { uint instanceIndex; uint geometryIndex; - uint primitiveOffset; + uint primitiveOffset; // either direct triangle offset within index buffer, or masked cluster index for clustered geo uint padding; }; +struct ShaderClusterTriangle +{ + uint packed; + void init(uint i0, uint i1, uint i2, uint flags = 0u) + { + packed = 0; + packed |= i0 & 0xFF; + packed |= (i1 & 0xFF) << 8u; + packed |= (i2 & 0xFF) << 16u; + packed |= (flags & 0xFF) << 24u; + } + uint i0() { return packed & 0xFF; } + uint i1() { return (packed >> 8u) & 0xFF; } + uint i2() { return (packed >> 16u) & 0xFF; } + uint3 tri() { return uint3(i0(), i1(), i2()); } + uint flags() { return packed >> 24u; } +}; +struct alignas(16) ShaderCluster +{ + uint triangleCount; + uint vertexCount; + uint padding0; + uint padding1; + + uint vertices[MESHLET_VERTEX_COUNT]; + ShaderClusterTriangle triangles[MESHLET_TRIANGLE_COUNT]; +}; + +struct alignas(16) ShaderSphere +{ + float3 center; + float radius; +}; + +struct alignas(16) ShaderClusterBounds +{ + ShaderSphere sphere; + + float3 cone_axis; + float cone_cutoff; +}; + struct alignas(16) ShaderTransform { float4 mat0; @@ -589,6 +634,7 @@ struct alignas(16) ShaderMeshInstance float4 quaternion; ShaderTransform transform; ShaderTransform transformPrev; + ShaderTransform transformRaw; // without quantization remapping applied void init() { @@ -612,6 +658,7 @@ struct alignas(16) ShaderMeshInstance quaternion = float4(0, 0, 0, 1); transform.init(); transformPrev.init(); + transformRaw.init(); } inline void SetUserStencilRef(uint stencilRef) @@ -845,11 +892,6 @@ struct alignas(16) ShaderEntity #endif // __cplusplus }; -struct alignas(16) ShaderSphere -{ - float3 center; - float radius; -}; struct alignas(16) ShaderFrustum { // Frustum planes: @@ -1078,6 +1120,7 @@ enum SHADERCAMERA_OPTIONS { SHADERCAMERA_OPTION_NONE = 0, SHADERCAMERA_OPTION_USE_SHADOW_MASK = 1 << 0, + SHADERCAMERA_OPTION_ORTHO = 1 << 1, }; struct alignas(16) ShaderCamera @@ -1166,8 +1209,10 @@ struct alignas(16) ShaderCamera int texture_vxgi_diffuse_index; int texture_vxgi_specular_index; - uint3 padding; + int texture_reprojected_depth_index; uint options; + uint padding0; + uint padding1; #ifdef __cplusplus void init() @@ -1233,6 +1278,9 @@ struct alignas(16) ShaderCamera texture_depth_index_prev = -1; texture_vxgi_diffuse_index = -1; texture_vxgi_specular_index = -1; + texture_reprojected_depth_index = -1; + + options = 0; } #else diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems b/WickedEngine/shaders/Shaders_SOURCE.vcxitems index 14916598f..7731f8237 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems @@ -35,6 +35,7 @@ + @@ -415,6 +416,14 @@ Compute 4.0 + + Compute + 4.0 + + + Compute + 4.0 + Pixel @@ -429,8 +438,28 @@ Compute 4.0 + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + Pixel @@ -477,6 +506,18 @@ Geometry Geometry + + Compute + 4.0 + + + Compute + 4.0 + + + Compute + 4.0 + Vertex Vertex diff --git a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters index cd42dde36..d7994d899 100644 --- a/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters +++ b/WickedEngine/shaders/Shaders_SOURCE.vcxitems.filters @@ -43,6 +43,9 @@ {2bb6f2d6-f3e1-4501-95bc-bd4a0ca64c4b} + + {ef9ff702-9bcf-43c1-a022-fdba26545709} + @@ -168,6 +171,9 @@ HF + + HF + @@ -1172,6 +1178,36 @@ CS + + AS + + + MS + + + MS + + + MS + + + MS + + + MS + + + MS + + + MS + + + CS + + + CS + diff --git a/WickedEngine/shaders/depth_pyramidCS.hlsl b/WickedEngine/shaders/depth_pyramidCS.hlsl new file mode 100644 index 000000000..f634b7a4f --- /dev/null +++ b/WickedEngine/shaders/depth_pyramidCS.hlsl @@ -0,0 +1,29 @@ +#include "globals.hlsli" +#include "ShaderInterop_Postprocess.h" + +PUSHCONSTANT(push, PostProcess); + +Texture2D input_depth : register(t0); +RWTexture2D output_mip0 : register(u0); +RWTexture2D output_mip1 : register(u1); + +[numthreads(8, 8, 1)] +void main(uint2 Gid : SV_GroupID, uint groupIndex : SV_GroupIndex) +{ + const uint2 GTid = remap_lane_8x8(groupIndex); + const uint2 pixel = clamp(Gid.xy * 8 + GTid.xy, 0, push.resolution - 1); + float2 uv = (pixel + 0.5) * push.resolution_rcp; + float4 depths = input_depth.GatherRed(sampler_linear_clamp, uv, 0); + float depth = min(depths.x, min(depths.y, min(depths.z, depths.w))); + + output_mip0[pixel] = depth; + + float depth_x = QuadReadAcrossX(depth); + float depth_y = QuadReadAcrossY(depth); + float depth_d = QuadReadAcrossDiagonal(depth); + depth = min(depth, min(depth_d, min(depth_x, depth_y))); + if (all((GTid % 2) == 0)) + { + output_mip1[pixel >> 1] = depth; + } +} diff --git a/WickedEngine/shaders/depth_reprojectCS.hlsl b/WickedEngine/shaders/depth_reprojectCS.hlsl new file mode 100644 index 000000000..1a52fc537 --- /dev/null +++ b/WickedEngine/shaders/depth_reprojectCS.hlsl @@ -0,0 +1,52 @@ +#include "globals.hlsli" +#include "ShaderInterop_Postprocess.h" + +PUSHCONSTANT(push, PostProcess); + +Texture2D input_depth : register(t0); +Texture2D input_velocity : register(t1); + +RWTexture2D output_mip0 : register(u0); +RWTexture2D output_mip1 : register(u1); + +[numthreads(8, 8, 1)] +void main(uint2 Gid : SV_GroupID, uint groupIndex : SV_GroupIndex) +{ + ShaderCamera camera = GetCamera(); + const uint2 GTid = remap_lane_8x8(groupIndex); + const uint2 pixel = clamp(Gid.xy * 8 + GTid.xy, 0, push.resolution - 1); + float2 uv = (pixel + 0.5) * push.resolution_rcp; + float2 velocity = input_velocity[pixel].xy; + float2 uv_prev = uv + velocity; + float depth = 0; + + if (is_saturated(uv_prev)) + { + float4 depth_prev = input_depth.GatherRed(sampler_linear_clamp, uv_prev); + depth = min(depth_prev.x, min(depth_prev.y, min(depth_prev.z, depth_prev.w))); + + float3 pos_prev = reconstruct_position(uv, depth, camera.previous_inverse_view_projection); + float4 pos = mul(camera.view_projection, float4(pos_prev, 1)); + + if (pos.w > 0) + { + pos.xyz /= pos.w; + depth = pos.z; + } + else + { + depth = 0; + } + } + + output_mip0[pixel] = depth; + + float depth_x = QuadReadAcrossX(depth); + float depth_y = QuadReadAcrossY(depth); + float depth_d = QuadReadAcrossDiagonal(depth); + depth = min(depth, min(depth_d, min(depth_x, depth_y))); + if (all((GTid % 2) == 0)) + { + output_mip1[pixel >> 1] = depth; + } +} diff --git a/WickedEngine/shaders/globals.hlsli b/WickedEngine/shaders/globals.hlsli index 97ea5b02a..9d0eba62d 100644 --- a/WickedEngine/shaders/globals.hlsli +++ b/WickedEngine/shaders/globals.hlsli @@ -366,6 +366,8 @@ RWTexture2D bindless_rwtextures_uint4[] : register(space36); static const BindlessResource> bindless_structured_meshinstance; static const BindlessResource> bindless_structured_geometry; static const BindlessResource> bindless_structured_meshlet; +static const BindlessResource> bindless_structured_cluster; +static const BindlessResource> bindless_structured_cluster_bounds; static const BindlessResource> bindless_structured_material; static const BindlessResource> bindless_structured_uint; static const BindlessResource> bindless_structured_terrain_chunks; @@ -373,6 +375,8 @@ static const BindlessResource> bindless_str [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_meshinstance[]; [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_geometry[]; [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_meshlet[]; +[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_cluster[]; +[[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_cluster_bounds[]; [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_material[]; [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_uint[]; [[vk::binding(0, DESCRIPTOR_SET_BINDLESS_STORAGE_BUFFER)]] StructuredBuffer bindless_structured_terrain_chunks[]; @@ -380,9 +384,11 @@ static const BindlessResource> bindless_str StructuredBuffer bindless_structured_meshinstance[] : register(space37); StructuredBuffer bindless_structured_geometry[] : register(space38); StructuredBuffer bindless_structured_meshlet[] : register(space39); -StructuredBuffer bindless_structured_material[] : register(space40); -StructuredBuffer bindless_structured_uint[] : register(space41); -StructuredBuffer bindless_structured_terrain_chunks[] : register(space42); +StructuredBuffer bindless_structured_cluster[] : register(space40); +StructuredBuffer bindless_structured_cluster_bounds[] : register(space41); +StructuredBuffer bindless_structured_material[] : register(space42); +StructuredBuffer bindless_structured_uint[] : register(space43); +StructuredBuffer bindless_structured_terrain_chunks[] : register(space44); #endif // __spirv__ inline FrameCB GetFrame() @@ -508,32 +514,34 @@ struct PrimitiveID uint primitiveIndex; uint instanceIndex; uint subsetIndex; + bool maybe_clustered; // These packing methods require meshlet data, and pack into 32 bits: inline uint pack() { - // 24 bit meshletIndex - // 8 bit meshletPrimitiveIndex + // 25 bit meshletIndex + // 7 bit meshletPrimitiveIndex ShaderMeshInstance inst = load_instance(instanceIndex); ShaderGeometry geometry = load_geometry(inst.geometryOffset + subsetIndex); uint meshletIndex = inst.meshletOffset + geometry.meshletOffset + primitiveIndex / MESHLET_TRIANGLE_COUNT; meshletIndex += 1; // indicate that it is valid - meshletIndex &= ~0u >> 8u; // mask 24 active bits + meshletIndex &= ~0u >> 7u; // mask 25 active bits uint meshletPrimitiveIndex = primitiveIndex % MESHLET_TRIANGLE_COUNT; - meshletPrimitiveIndex &= 0xFF; // mask 8 active bits - meshletPrimitiveIndex <<= 24u; + meshletPrimitiveIndex &= 0x7F; // mask 7 active bits + meshletPrimitiveIndex <<= 25u; return meshletPrimitiveIndex | meshletIndex; } inline void unpack(uint value) { - uint meshletIndex = value & (~0u >> 8u); + uint meshletIndex = value & (~0u >> 7u); meshletIndex -= 1; // remove valid check - uint meshletPrimitiveIndex = (value >> 24u) & 0xFF; + uint meshletPrimitiveIndex = (value >> 25u) & 0x7F; ShaderMeshlet meshlet = load_meshlet(meshletIndex); ShaderMeshInstance inst = load_instance(meshlet.instanceIndex); primitiveIndex = meshlet.primitiveOffset + meshletPrimitiveIndex; instanceIndex = meshlet.instanceIndex; subsetIndex = meshlet.geometryIndex - inst.geometryOffset; + maybe_clustered = true; } // These packing methods don't need meshlets, but they are packed into 64 bits: @@ -549,7 +557,33 @@ struct PrimitiveID primitiveIndex = value.x - 1; // remove valid check instanceIndex = value.y & 0xFFFFFF; subsetIndex = (value.y >> 24u) & 0xFF; + maybe_clustered = false; } + + uint3 tri() + { + ShaderMeshInstance inst = load_instance(instanceIndex); + ShaderGeometry geometry = load_geometry(inst.geometryOffset + subsetIndex); + if (maybe_clustered && geometry.vb_clu >= 0) + { + const uint clusterID = primitiveIndex >> 7u; + const uint triangleID = primitiveIndex & 0x7F; + ShaderCluster cluster = bindless_structured_cluster[geometry.vb_clu][clusterID]; + uint i0 = cluster.vertices[cluster.triangles[triangleID].i0()]; + uint i1 = cluster.vertices[cluster.triangles[triangleID].i1()]; + uint i2 = cluster.vertices[cluster.triangles[triangleID].i2()]; + return uint3(i0, i1, i2); + } + const uint startIndex = primitiveIndex * 3 + geometry.indexOffset; + Buffer indexBuffer = bindless_buffers_uint[NonUniformResourceIndex(geometry.ib)]; + uint i0 = indexBuffer[startIndex + 0]; + uint i1 = indexBuffer[startIndex + 1]; + uint i2 = indexBuffer[startIndex + 2]; + return uint3(i0, i1, i2); + } + uint i0() { return tri().x; } + uint i1() { return tri().y; } + uint i2() { return tri().z; } }; #define texture_random64x64 bindless_textures[GetFrame().texture_random64x64_index] diff --git a/WickedEngine/shaders/meshlet_prepareCS.hlsl b/WickedEngine/shaders/meshlet_prepareCS.hlsl index 03060f4f6..8a7158bfc 100644 --- a/WickedEngine/shaders/meshlet_prepareCS.hlsl +++ b/WickedEngine/shaders/meshlet_prepareCS.hlsl @@ -9,6 +9,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn { uint instanceIndex = Gid.x; ShaderMeshInstance inst = load_instance(instanceIndex); + ShaderGeometry mesh = load_geometry(inst.baseGeometryOffset); for (uint i = 0; i < inst.baseGeometryCount; ++i) { @@ -16,11 +17,18 @@ void main(uint3 DTid : SV_DispatchThreadID, uint3 Gid : SV_GroupID, uint groupIn ShaderGeometry geometry = load_geometry(geometryIndex); for (uint j = groupIndex; j < geometry.meshletCount; j += THREADCOUNT) { - ShaderMeshlet meshlet; + ShaderMeshlet meshlet = (ShaderMeshlet)0; meshlet.instanceIndex = instanceIndex; meshlet.geometryIndex = geometryIndex; - meshlet.primitiveOffset = j * MESHLET_TRIANGLE_COUNT; - meshlet.padding = 0; + + if (geometry.vb_clu < 0) + { + meshlet.primitiveOffset = j * MESHLET_TRIANGLE_COUNT; + } + else + { + meshlet.primitiveOffset = (geometry.meshletOffset + j - mesh.meshletOffset) << 7u; + } uint meshletIndex = inst.meshletOffset + geometry.meshletOffset + j; output_meshlets[meshletIndex] = meshlet; diff --git a/WickedEngine/shaders/objectAS.hlsl b/WickedEngine/shaders/objectAS.hlsl new file mode 100644 index 000000000..723fac5d4 --- /dev/null +++ b/WickedEngine/shaders/objectAS.hlsl @@ -0,0 +1,2 @@ +#define OBJECTSHADER_COMPILE_AS +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/objectHF.hlsli b/WickedEngine/shaders/objectHF.hlsli index e81da927a..8937cc9ee 100644 --- a/WickedEngine/shaders/objectHF.hlsli +++ b/WickedEngine/shaders/objectHF.hlsli @@ -322,6 +322,7 @@ struct PixelInput half wet : WET; #endif // OBJECTSHADER_USE_WETMAP +#ifndef OBJECTSHADER_COMPILE_MS #ifdef OBJECTSHADER_USE_RENDERTARGETARRAYINDEX #ifdef VPRT_EMULATION uint RTIndex : RTINDEX; @@ -329,7 +330,9 @@ struct PixelInput uint RTIndex : SV_RenderTargetArrayIndex; #endif // VPRT_EMULATION #endif // OBJECTSHADER_USE_RENDERTARGETARRAYINDEX +#endif // OBJECTSHADER_COMPILE_MS +#ifndef OBJECTSHADER_COMPILE_MS #ifdef OBJECTSHADER_USE_VIEWPORTARRAYINDEX #ifdef VPRT_EMULATION uint VPIndex : VPINDEX; @@ -337,6 +340,7 @@ struct PixelInput uint VPIndex : SV_ViewportArrayIndex; #endif // VPRT_EMULATION #endif // OBJECTSHADER_USE_VIEWPORTARRAYINDEX +#endif // OBJECTSHADER_COMPILE_MS #ifdef OBJECTSHADER_USE_INSTANCEINDEX inline uint GetInstanceIndex() @@ -362,20 +366,13 @@ struct PixelInput #endif // OBJECTSHADER_USE_UVSETS }; - -// OBJECT SHADER PROTOTYPE -/////////////////////////// - -#ifdef OBJECTSHADER_COMPILE_VS - -// Vertex shader base: -PixelInput main(VertexInput input) +PixelInput vertex_to_pixel_export(VertexInput input) { - PixelInput Out; - VertexSurface surface; surface.create(GetMaterial(), input); + PixelInput Out; + Out.pos = surface.position; #ifndef OBJECTSHADER_USE_NOCAMERA @@ -430,7 +427,9 @@ PixelInput main(VertexInput input) #ifdef OBJECTSHADER_USE_RENDERTARGETARRAYINDEX const uint frustum_index = input.GetInstancePointer().GetCameraIndex(); +#ifndef OBJECTSHADER_COMPILE_MS Out.RTIndex = GetCamera(frustum_index).output_index; +#endif // OBJECTSHADER_COMPILE_MS #ifndef OBJECTSHADER_USE_NOCAMERA Out.pos = mul(GetCamera(frustum_index).view_projection, surface.position); #endif // OBJECTSHADER_USE_NOCAMERA @@ -438,7 +437,9 @@ PixelInput main(VertexInput input) #ifdef OBJECTSHADER_USE_VIEWPORTARRAYINDEX const uint frustum_index = input.GetInstancePointer().GetCameraIndex(); +#ifndef OBJECTSHADER_COMPILE_MS Out.VPIndex = GetCamera(frustum_index).output_index; +#endif // OBJECTSHADER_COMPILE_MS #ifndef OBJECTSHADER_USE_NOCAMERA Out.pos = mul(GetCamera(frustum_index).view_projection, surface.position); #endif // OBJECTSHADER_USE_NOCAMERA @@ -447,6 +448,18 @@ PixelInput main(VertexInput input) return Out; } + +// OBJECT SHADER PROTOTYPE +/////////////////////////// + +#ifdef OBJECTSHADER_COMPILE_VS + +// Vertex shader base: +PixelInput main(VertexInput input) +{ + return vertex_to_pixel_export(input); +} + #endif // OBJECTSHADER_COMPILE_VS diff --git a/WickedEngine/shaders/objectHF_mesh_shading.hlsli b/WickedEngine/shaders/objectHF_mesh_shading.hlsli new file mode 100644 index 000000000..df69852b3 --- /dev/null +++ b/WickedEngine/shaders/objectHF_mesh_shading.hlsli @@ -0,0 +1,228 @@ +#include "globals.hlsli" +#include "objectHF.hlsli" + +#define FRUSTUM_CULLING +#define CONE_CULLING +#define ZERO_AREA_CULLING +#define OCCLUSION_CULLING + +static const uint AS_GROUPSIZE = 32; +static const uint MS_GROUPSIZE = 128; + +struct AmplificationPayload +{ + uint instanceID; + uint meshletGroupOffset; + min16uint meshlets[AS_GROUPSIZE]; // this could be uint8_t if HLSL supported it because they are relative to meshletGroupOffset, maybe in the future it will be a reality +}; + +#ifdef OBJECTSHADER_COMPILE_AS +groupshared AmplificationPayload amplification_payload; + +[numthreads(AS_GROUPSIZE, 1, 1)] +void main(uint3 Gid : SV_GroupID, uint groupIndex : SV_GroupIndex) +{ + ShaderGeometry geometry = GetMesh(); + + uint instanceID = Gid.y; + uint meshletGroupOffset = geometry.meshletOffset + Gid.x * AS_GROUPSIZE; + + if(WaveIsFirstLane()) + { + amplification_payload.instanceID = instanceID; + amplification_payload.meshletGroupOffset = meshletGroupOffset; + } + + ShaderMeshInstancePointer poi = bindless_buffers[push.instances].Load(push.instance_offset + instanceID * sizeof(ShaderMeshInstancePointer)); + ShaderMeshInstance inst = load_instance(poi.GetInstanceIndex()); + + uint meshletID = meshletGroupOffset + groupIndex; + + bool visible = meshletID < geometry.meshletOffset + geometry.meshletCount; + + // Meshlet culling: + if (visible) + { + ShaderCamera camera = GetCamera(poi.GetCameraIndex()); + ShaderClusterBounds bounds = bindless_structured_cluster_bounds[geometry.vb_bou][meshletID]; + if (geometry.vb_pre >= 0) + { + // when object is skinned, we take the whole instance bounds, because skinned meshlet transforms are not computed + bounds.sphere.center = inst.center; + bounds.sphere.radius = inst.radius; + } + else + { + bounds.sphere.center = mul(inst.transformRaw.GetMatrix(), float4(bounds.sphere.center, 1)).xyz; + bounds.sphere.radius = max3(mul((float3x3)inst.transformRaw.GetMatrix(), bounds.sphere.radius.xxx)); + } + + // Only allow culling when camera is not inside: + if (distance(camera.position, bounds.sphere.center) > bounds.sphere.radius) + { + +#ifdef CONE_CULLING + if((geometry.flags & SHADERMESH_FLAG_DOUBLE_SIDED) == 0 && geometry.vb_pre < 0) // disable cone culling for double sided and skinned + { + // Cone culling: + bounds.cone_axis = rotate_vector(bounds.cone_axis, inst.quaternion); + if (camera.options & SHADERCAMERA_OPTION_ORTHO) + { + visible &= dot(camera.forward, bounds.cone_axis) < bounds.cone_cutoff; + } + else + { + visible &= dot(bounds.sphere.center - camera.position, bounds.cone_axis) < bounds.cone_cutoff * length(bounds.sphere.center - camera.position) + bounds.sphere.radius; + } + } +#endif // CONE_CULLING + +#ifdef FRUSTUM_CULLING + if (visible) + { + // Frustum culling: + visible &= camera.frustum.intersects(bounds.sphere); + } +#endif // FRUSTUM_CULLING + + if (visible && camera.texture_reprojected_depth_index >= 0) + { + Texture2D reprojected_depth = bindless_textures[camera.texture_reprojected_depth_index]; + float cam_sphere_distance = length(bounds.sphere.center - camera.position); + float radius = cam_sphere_distance * tan(asin(bounds.sphere.radius / cam_sphere_distance)); // perspective distortion https://www.nickdarnell.com/hierarchical-z-buffer-occlusion-culling/ + float3 up_radius = camera.up * radius; + float3 right = cross(camera.forward, camera.up); + float3 right_radius = right * radius; + + float3 corner_0_world = bounds.sphere.center + up_radius - right_radius; + float3 corner_1_world = bounds.sphere.center + up_radius + right_radius; + float3 corner_2_world = bounds.sphere.center - up_radius - right_radius; + float3 corner_3_world = bounds.sphere.center - up_radius + right_radius; + + float4 corner_0_clip = mul(camera.previous_view_projection, float4(corner_0_world, 1)); + float4 corner_1_clip = mul(camera.previous_view_projection, float4(corner_1_world, 1)); + float4 corner_2_clip = mul(camera.previous_view_projection, float4(corner_2_world, 1)); + float4 corner_3_clip = mul(camera.previous_view_projection, float4(corner_3_world, 1)); + + float2 corner_0_uv = clipspace_to_uv(corner_0_clip.xy / corner_0_clip.w); + float2 corner_1_uv = clipspace_to_uv(corner_1_clip.xy / corner_1_clip.w); + float2 corner_2_uv = clipspace_to_uv(corner_2_clip.xy / corner_2_clip.w); + float2 corner_3_uv = clipspace_to_uv(corner_3_clip.xy / corner_3_clip.w); + + float sphere_width_uv = length(corner_0_uv - corner_1_uv); + + float3 sphere_center_view = mul(camera.previous_view, float4(bounds.sphere.center, 1)).xyz; + float3 pv = sphere_center_view - normalize(sphere_center_view) * bounds.sphere.radius; + float4 closest_sphere_point = mul(camera.previous_projection, float4(pv, 1)); + + float w = sphere_width_uv * max(camera.internal_resolution.x, camera.internal_resolution.y); + +#ifdef ZERO_AREA_CULLING + if (w < 1) + visible = false; +#endif // ZERO_AREA_CULLING + +#ifdef OCCLUSION_CULLING + if (visible && closest_sphere_point.w > 0) + { + float lod = ceil(log2(w)); + float4 depths = float4( + reprojected_depth.SampleLevel(sampler_point_clamp, corner_0_uv, lod).r, + reprojected_depth.SampleLevel(sampler_point_clamp, corner_1_uv, lod).r, + reprojected_depth.SampleLevel(sampler_point_clamp, corner_2_uv, lod).r, + reprojected_depth.SampleLevel(sampler_point_clamp, corner_3_uv, lod).r + ); + float min_depth = min(depths.x, min(depths.y, min(depths.z, depths.w))); + float sphere_depth = closest_sphere_point.z / closest_sphere_point.w; + if (sphere_depth < min_depth - 0.01) // little safety bias + visible = false; + } +#endif // OCCLUSION_CULLING + } + + } + } + + if (visible) + { + uint index = WavePrefixCountBits(visible); + amplification_payload.meshlets[index] = meshletID - meshletGroupOffset; // relative packed + } + + uint visibleCount = WaveActiveCountBits(visible); + + DispatchMesh(visibleCount, 1, 1, amplification_payload); +} +#endif // OBJECTSHADER_COMPILE_AS + +#ifdef OBJECTSHADER_COMPILE_MS + +struct PrimitiveAttributes +{ +#if defined(OBJECTSHADER_LAYOUT_PREPASS) || defined(OBJECTSHADER_LAYOUT_PREPASS_TEX) + uint primitiveID : SV_PrimitiveID; +#endif // defined(OBJECTSHADER_LAYOUT_PREPASS) || defined(OBJECTSHADER_LAYOUT_PREPASS_TEX) + +#ifdef OBJECTSHADER_USE_RENDERTARGETARRAYINDEX + uint RTIndex : SV_RenderTargetArrayIndex; +#endif // OBJECTSHADER_USE_RENDERTARGETARRAYINDEX + +#ifdef OBJECTSHADER_USE_VIEWPORTARRAYINDEX + uint VPIndex : SV_ViewportArrayIndex; +#endif // OBJECTSHADER_USE_VIEWPORTARRAYINDEX +}; + +[outputtopology("triangle")] +[numthreads(MS_GROUPSIZE, 1, 1)] +void main( + uint DTid : SV_DispatchThreadID, + uint Gid : SV_GroupID, + uint groupIndex : SV_GroupIndex, + in payload AmplificationPayload amplification_payload, + out vertices PixelInput verts[MESHLET_VERTEX_COUNT], + out indices uint3 triangles[MESHLET_TRIANGLE_COUNT] +#if defined(OBJECTSHADER_LAYOUT_PREPASS) || defined(OBJECTSHADER_LAYOUT_PREPASS_TEX) || defined(OBJECTSHADER_USE_RENDERTARGETARRAYINDEX) || defined(OBJECTSHADER_USE_VIEWPORTARRAYINDEX) + ,out primitives PrimitiveAttributes primitives[MESHLET_TRIANGLE_COUNT] +#endif + ) +{ + uint meshletGroupOffset = amplification_payload.meshletGroupOffset; + uint meshletID = meshletGroupOffset + amplification_payload.meshlets[Gid]; + ShaderGeometry geometry = GetMesh(); + if(meshletID >= geometry.meshletOffset + geometry.meshletCount) + return; + ShaderCluster cluster = bindless_structured_cluster[geometry.vb_clu][meshletID]; + ShaderMeshInstancePointer poi = bindless_buffers[push.instances].Load(push.instance_offset + amplification_payload.instanceID * sizeof(ShaderMeshInstancePointer)); + SetMeshOutputCounts(cluster.vertexCount, cluster.triangleCount); + const uint vi = groupIndex; + if (vi < cluster.vertexCount) + { + uint vertexID = cluster.vertices[vi]; + + VertexInput input; + input.vertexID = cluster.vertices[vi]; + input.instanceID = amplification_payload.instanceID; + + verts[vi] = vertex_to_pixel_export(input); + } + const uint ti = MESHLET_TRIANGLE_COUNT - 1 - groupIndex; // reverse the order of threads, so those which are not writing vertices are utilized earlier + if (ti < cluster.triangleCount) + { + triangles[ti] = cluster.triangles[ti].tri(); + +#if defined(OBJECTSHADER_LAYOUT_PREPASS) || defined(OBJECTSHADER_LAYOUT_PREPASS_TEX) + primitives[ti].primitiveID = (meshletID - geometry.meshletOffset) * MESHLET_TRIANGLE_COUNT + ti; +#endif // defined(OBJECTSHADER_LAYOUT_PREPASS) || defined(OBJECTSHADER_LAYOUT_PREPASS_TEX) + +#ifdef OBJECTSHADER_USE_RENDERTARGETARRAYINDEX + const uint frustum_index = poi.GetCameraIndex(); + primitives[ti].RTIndex = GetCamera(frustum_index).output_index; +#endif // OBJECTSHADER_USE_RENDERTARGETARRAYINDEX + +#ifdef OBJECTSHADER_USE_VIEWPORTARRAYINDEX + const uint frustum_index = poi.GetCameraIndex(); + primitives[ti].VPIndex = GetCamera(frustum_index).output_index; +#endif // OBJECTSHADER_USE_VIEWPORTARRAYINDEX + } +} +#endif // OBJECTSHADER_COMPILE_MS diff --git a/WickedEngine/shaders/objectMS.hlsl b/WickedEngine/shaders/objectMS.hlsl new file mode 100644 index 000000000..0a1fcbc9a --- /dev/null +++ b/WickedEngine/shaders/objectMS.hlsl @@ -0,0 +1,3 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_COMMON +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/objectMS_prepass.hlsl b/WickedEngine/shaders/objectMS_prepass.hlsl new file mode 100644 index 000000000..0d0dac56b --- /dev/null +++ b/WickedEngine/shaders/objectMS_prepass.hlsl @@ -0,0 +1,3 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_PREPASS +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/objectMS_prepass_alphatest.hlsl b/WickedEngine/shaders/objectMS_prepass_alphatest.hlsl new file mode 100644 index 000000000..e8a5638bc --- /dev/null +++ b/WickedEngine/shaders/objectMS_prepass_alphatest.hlsl @@ -0,0 +1,3 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_PREPASS_TEX +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/objectMS_simple.hlsl b/WickedEngine/shaders/objectMS_simple.hlsl new file mode 100644 index 000000000..5001826c9 --- /dev/null +++ b/WickedEngine/shaders/objectMS_simple.hlsl @@ -0,0 +1,4 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_SHADOW_TEX +#define OBJECTSHADER_USE_COLOR +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/shadowMS.hlsl b/WickedEngine/shaders/shadowMS.hlsl new file mode 100644 index 000000000..e09933970 --- /dev/null +++ b/WickedEngine/shaders/shadowMS.hlsl @@ -0,0 +1,4 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_SHADOW +#define OBJECTSHADER_USE_VIEWPORTARRAYINDEX +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/shadowMS_alphatest.hlsl b/WickedEngine/shaders/shadowMS_alphatest.hlsl new file mode 100644 index 000000000..1fb28e69c --- /dev/null +++ b/WickedEngine/shaders/shadowMS_alphatest.hlsl @@ -0,0 +1,4 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_SHADOW_TEX +#define OBJECTSHADER_USE_VIEWPORTARRAYINDEX +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/shadowMS_transparent.hlsl b/WickedEngine/shaders/shadowMS_transparent.hlsl new file mode 100644 index 000000000..3e56c36f5 --- /dev/null +++ b/WickedEngine/shaders/shadowMS_transparent.hlsl @@ -0,0 +1,5 @@ +#define OBJECTSHADER_COMPILE_MS +#define OBJECTSHADER_LAYOUT_SHADOW_TEX +#define OBJECTSHADER_USE_COLOR +#define OBJECTSHADER_USE_VIEWPORTARRAYINDEX +#include "objectHF_mesh_shading.hlsli" diff --git a/WickedEngine/shaders/surfaceHF.hlsli b/WickedEngine/shaders/surfaceHF.hlsli index 322a8ee0d..bf37f7678 100644 --- a/WickedEngine/shaders/surfaceHF.hlsli +++ b/WickedEngine/shaders/surfaceHF.hlsli @@ -344,11 +344,9 @@ struct Surface layerMask = material.layerMask & inst.layerMask; - const uint startIndex = prim.primitiveIndex * 3 + geometry.indexOffset; - Buffer indexBuffer = bindless_buffers_uint[NonUniformResourceIndex(geometry.ib)]; - i0 = indexBuffer[startIndex + 0]; - i1 = indexBuffer[startIndex + 1]; - i2 = indexBuffer[startIndex + 2]; + i0 = prim.i0(); + i1 = prim.i1(); + i2 = prim.i2(); Buffer buf = bindless_buffers_float4[NonUniformResourceIndex(geometry.vb_pos_wind)]; data0 = buf[i0]; diff --git a/WickedEngine/wiApplication.cpp b/WickedEngine/wiApplication.cpp index c3f4c66d0..ab32ee8c1 100644 --- a/WickedEngine/wiApplication.cpp +++ b/WickedEngine/wiApplication.cpp @@ -485,6 +485,10 @@ namespace wi infodisplay_str += std::to_string(graphicsDevice->GetActivePipelineCount()); infodisplay_str += "\n"; } + if (infoDisplay.pipeline_creation && wi::renderer::IsPipelineCreationActive()) + { + infodisplay_str += "Shader pipeline creation is running...\n"; + } wi::font::Params params = wi::font::Params( 4 + canvas.PhysicalToLogical((uint32_t)infoDisplay.rect.left), diff --git a/WickedEngine/wiApplication.h b/WickedEngine/wiApplication.h index be800a4c4..5b13c4236 100644 --- a/WickedEngine/wiApplication.h +++ b/WickedEngine/wiApplication.h @@ -111,6 +111,8 @@ namespace wi bool heap_allocation_counter = false; // display the active graphics pipeline count bool pipeline_count = false; + // display the pipeline creation info + bool pipeline_creation = false; // display video memory usage and budget bool vram_usage = false; // text size diff --git a/WickedEngine/wiEnums.h b/WickedEngine/wiEnums.h index 99f891b2b..656f5c68a 100644 --- a/WickedEngine/wiEnums.h +++ b/WickedEngine/wiEnums.h @@ -413,6 +413,20 @@ namespace wi::enums CSTYPE_YUV_TO_RGB, CSTYPE_WETMAP_UPDATE, CSTYPE_CAUSTICS, + CSTYPE_DEPTH_REPROJECT, + CSTYPE_DEPTH_PYRAMID, + + + ASTYPE_OBJECT, + + + MSTYPE_OBJECT, + MSTYPE_OBJECT_PREPASS, + MSTYPE_OBJECT_PREPASS_ALPHATEST, + MSTYPE_OBJECT_SIMPLE, + MSTYPE_SHADOW, + MSTYPE_SHADOW_ALPHATEST, + MSTYPE_SHADOW_TRANSPARENT, // raytracing pipelines: diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index b5427cc20..fd506cf8e 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -5265,7 +5265,6 @@ using namespace vulkan_internal; auto shader_internal = to_internal(shader); uint32_t i = 0; - size_t check_max = internal_state->layoutBindings.size(); // dont't check for duplicates within self table for (auto& x : shader_internal->layoutBindings) { bool found = false; @@ -5283,8 +5282,6 @@ using namespace vulkan_internal; y.stageFlags |= x.stageFlags; break; } - if (j++ >= check_max) - break; } if (!found) diff --git a/WickedEngine/wiRenderPath3D.cpp b/WickedEngine/wiRenderPath3D.cpp index fa8b45056..5b7b9185d 100644 --- a/WickedEngine/wiRenderPath3D.cpp +++ b/WickedEngine/wiRenderPath3D.cpp @@ -52,6 +52,7 @@ namespace wi depthBuffer_Copy1 = {}; depthBuffer_Reflection = {}; rtLinearDepth = {}; + reprojectedDepth = {}; debugUAV = {}; tiledLightResources = {}; @@ -81,6 +82,7 @@ namespace wi void RenderPath3D::ResizeBuffers() { + first_frame = true; DeleteGPUResources(); GraphicsDevice* device = wi::graphics::GetDevice(); @@ -564,6 +566,33 @@ namespace wi vxgiResources = {}; } + // Check whether reprojected depth is required: + if (!first_frame && wi::renderer::IsMeshShaderAllowed() && wi::renderer::IsMeshletOcclusionCullingEnabled()) + { + TextureDesc desc; + desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; + desc.format = Format::R16_UNORM; + desc.width = internalResolution.x; + desc.height = internalResolution.y; + desc.mip_levels = GetMipCount(desc.width, desc.height, 1, 4); + desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE; + device->CreateTexture(&desc, nullptr, &reprojectedDepth); + device->SetName(&reprojectedDepth, "reprojectedDepth"); + + for (uint32_t i = 0; i < reprojectedDepth.desc.mip_levels; ++i) + { + int subresource_index; + subresource_index = device->CreateSubresource(&reprojectedDepth, SubresourceType::SRV, 0, 1, i, 1); + assert(subresource_index == i); + subresource_index = device->CreateSubresource(&reprojectedDepth, SubresourceType::UAV, 0, 1, i, 1); + assert(subresource_index == i); + } + } + else + { + reprojectedDepth = {}; + } + // Check whether velocity buffer is required: if ( getMotionBlurEnabled() || @@ -575,7 +604,8 @@ namespace wi wi::renderer::GetRaytracedShadowsEnabled() || getAO() == AO::AO_RTAO || wi::renderer::GetVariableRateShadingClassification() || - getFSR2Enabled() + getFSR2Enabled() || + reprojectedDepth.IsValid() ) { if (!rtVelocity.IsValid()) @@ -741,6 +771,7 @@ namespace wi { camera->texture_vxgi_specular_index = -1; } + camera->texture_reprojected_depth_index = device->GetDescriptorIndex(&reprojectedDepth, SubresourceType::SRV); camera_reflection.canvas.init(*this); camera_reflection.width = (float)depthBuffer_Reflection.desc.width; @@ -770,6 +801,7 @@ namespace wi camera_reflection.texture_surfelgi_index = -1; camera_reflection.texture_vxgi_diffuse_index = -1; camera_reflection.texture_vxgi_specular_index = -1; + camera_reflection.texture_reprojected_depth_index = -1; video_cmd = {}; if (getSceneUpdateEnabled() && scene->videos.GetCount() > 0) @@ -916,6 +948,16 @@ namespace wi wi::renderer::RefreshImpostors(*scene, cmd); + if (reprojectedDepth.IsValid()) + { + wi::renderer::ComputeReprojectedDepthPyramid( + depthBuffer_Copy, + rtVelocity, + reprojectedDepth, + cmd + ); + } + RenderPassImage rp[] = { RenderPassImage::DepthStencil( &depthBuffer_Main, @@ -1665,6 +1707,8 @@ namespace wi RenderPath2D::Render(); wi::jobsystem::Wait(ctx); + + first_frame = false; } void RenderPath3D::Compose(CommandList cmd) const diff --git a/WickedEngine/wiRenderPath3D.h b/WickedEngine/wiRenderPath3D.h index bd642811b..425435849 100644 --- a/WickedEngine/wiRenderPath3D.h +++ b/WickedEngine/wiRenderPath3D.h @@ -83,6 +83,8 @@ namespace wi bool fsrEnabled = false; bool fsr2Enabled = false; + mutable bool first_frame = true; + public: wi::graphics::Texture rtMain; wi::graphics::Texture rtMain_render; // can be MSAA @@ -117,6 +119,7 @@ namespace wi wi::graphics::Texture depthBuffer_Copy1; // used for disocclusion check wi::graphics::Texture depthBuffer_Reflection; // used for reflection, single sample wi::graphics::Texture rtLinearDepth; // linear depth result + mipchain (max filter) + wi::graphics::Texture reprojectedDepth; // prev frame depth reprojected into current, and downsampled for meshlet occlusion culling wi::graphics::Texture debugUAV; // debug UAV can be used by some shaders... wi::renderer::TiledLightResources tiledLightResources; diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index ed4f2b049..1d1c706e7 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -137,6 +137,8 @@ bool DDGI_DEBUG_ENABLED = false; uint32_t DDGI_RAYCOUNT = 256u; float DDGI_BLEND_SPEED = 0.1f; float GI_BOOST = 1.0f; +bool MESH_SHADER_ALLOWED = false; +bool MESHLET_OCCLUSION_CULLING = false; std::atomic SHADER_ERRORS{ 0 }; std::atomic SHADER_MISSING{ 0 }; bool VXGI_ENABLED = false; @@ -347,6 +349,13 @@ const Texture* GetTexture(TEXTYPES id) return &textures[id]; } +enum OBJECT_MESH_SHADER_PSO +{ + OBJECT_MESH_SHADER_PSO_DISABLED, + OBJECT_MESH_SHADER_PSO_ENABLED, + OBJECT_MESH_SHADER_PSO_COUNT +}; + union ObjectRenderingVariant { struct @@ -358,6 +367,7 @@ union ObjectRenderingVariant uint32_t tessellation : 1; // bool uint32_t alphatest : 1; // bool uint32_t sample_count : 4; // 1, 2, 4, 8 + uint32_t mesh_shader : 1; // bool } bits; uint32_t value; }; @@ -367,9 +377,10 @@ inline PipelineState* GetObjectPSO(ObjectRenderingVariant variant) { return &PSO_object[variant.bits.renderpass][variant.bits.shadertype][variant.value]; } -wi::jobsystem::context object_pso_job_ctx[RENDERPASS_COUNT]; +wi::jobsystem::context object_pso_job_ctx[RENDERPASS_COUNT][OBJECT_MESH_SHADER_PSO_COUNT]; PipelineState PSO_object_wire; PipelineState PSO_object_wire_tessellation; +PipelineState PSO_object_wire_mesh_shader; wi::vector customShaders; int RegisterCustomShader(const CustomShader& customShader) @@ -385,7 +396,60 @@ const wi::vector& GetCustomShaders() return customShaders; } +SHADERTYPE GetASTYPE(RENDERPASS renderPass, bool tessellation, bool alphatest, bool transparent, bool mesh_shader) +{ + if (!mesh_shader) + return SHADERTYPE_COUNT; + return ASTYPE_OBJECT; +} +SHADERTYPE GetMSTYPE(RENDERPASS renderPass, bool tessellation, bool alphatest, bool transparent, bool mesh_shader) +{ + if (!mesh_shader) + return SHADERTYPE_COUNT; + + SHADERTYPE realMS = SHADERTYPE_COUNT; + + switch (renderPass) + { + case RENDERPASS_MAIN: + realMS = MSTYPE_OBJECT; + break; + case RENDERPASS_PREPASS: + case RENDERPASS_PREPASS_DEPTHONLY: + if (alphatest) + { + realMS = MSTYPE_OBJECT_PREPASS_ALPHATEST; + } + else + { + realMS = MSTYPE_OBJECT_PREPASS; + } + break; + case RENDERPASS_SHADOW: + if (transparent) + { + realMS = MSTYPE_SHADOW_TRANSPARENT; + } + else + { + if (alphatest) + { + realMS = MSTYPE_SHADOW_ALPHATEST; + } + else + { + realMS = MSTYPE_SHADOW; + } + } + break; + case RENDERPASS_RAINBLOCKER: + realMS = MSTYPE_SHADOW; + break; + } + + return realMS; +} SHADERTYPE GetVSTYPE(RENDERPASS renderPass, bool tessellation, bool alphatest, bool transparent) { SHADERTYPE realVS = VSTYPE_OBJECT_SIMPLE; @@ -1143,6 +1207,8 @@ void LoadShaders() wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_YUV_TO_RGB], "yuv_to_rgbCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_WETMAP_UPDATE], "wetmap_updateCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_CAUSTICS], "causticsCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_DEPTH_REPROJECT], "depth_reprojectCS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::CS, shaders[CSTYPE_DEPTH_PYRAMID], "depth_pyramidCS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::HS, shaders[HSTYPE_OBJECT], "objectHS.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::HS, shaders[HSTYPE_OBJECT_PREPASS], "objectHS_prepass.cso"); }); @@ -1154,6 +1220,20 @@ void LoadShaders() wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::DS, shaders[DSTYPE_OBJECT_PREPASS_ALPHATEST], "objectDS_prepass_alphatest.cso"); }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::DS, shaders[DSTYPE_OBJECT_SIMPLE], "objectDS_simple.cso"); }); + if (device->CheckCapability(GraphicsDeviceCapability::MESH_SHADER)) + { + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::AS, shaders[ASTYPE_OBJECT], "objectAS.cso"); }); + + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_OBJECT], "objectMS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_OBJECT_PREPASS], "objectMS_prepass.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_OBJECT_PREPASS_ALPHATEST], "objectMS_prepass_alphatest.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_OBJECT_SIMPLE], "objectMS_simple.cso"); }); + + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_SHADOW], "shadowMS.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_SHADOW_ALPHATEST], "shadowMS_alphatest.cso"); }); + wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { LoadShader(ShaderStage::MS, shaders[MSTYPE_SHADOW_TRANSPARENT], "shadowMS_transparent.cso"); }); + } + wi::jobsystem::Dispatch(ctx, MaterialComponent::SHADERTYPE_COUNT, 1, [](wi::jobsystem::JobArgs args) { LoadShader( @@ -1235,6 +1315,17 @@ void LoadShaders() desc.hs = &shaders[HSTYPE_OBJECT_SIMPLE]; desc.ds = &shaders[DSTYPE_OBJECT_SIMPLE]; device->CreatePipelineState(&desc, &PSO_object_wire_tessellation); + + if (device->CheckCapability(GraphicsDeviceCapability::MESH_SHADER)) + { + desc.vs = {}; + desc.hs = {}; + desc.ds = {}; + desc.pt = PrimitiveTopology::TRIANGLELIST; + desc.as = &shaders[ASTYPE_OBJECT]; + desc.ms = &shaders[MSTYPE_OBJECT_SIMPLE]; + device->CreatePipelineState(&desc, &PSO_object_wire_mesh_shader); + } }); wi::jobsystem::Execute(ctx, [](wi::jobsystem::JobArgs args) { PipelineStateDesc desc; @@ -1743,233 +1834,267 @@ void LoadShaders() for (uint32_t renderPass = 0; renderPass < RENDERPASS_COUNT; ++renderPass) { - // default objectshaders: - // We don't wait for these here, because then it can slow down the init time a lot - // We will wait for these to complete in RenderMeshes() just before they will be first used - wi::jobsystem::Wait(object_pso_job_ctx[renderPass]); - for (uint32_t shaderType = 0; shaderType < MaterialComponent::SHADERTYPE_COUNT; ++shaderType) + for (uint32_t mesh_shader = 0; mesh_shader <= (device->CheckCapability(GraphicsDeviceCapability::MESH_SHADER) ? 1u : 0u); ++mesh_shader) { - wi::jobsystem::Execute(object_pso_job_ctx[renderPass], [=](wi::jobsystem::JobArgs args) { - for (uint32_t blendMode = 0; blendMode < BLENDMODE_COUNT; ++blendMode) - { - for (uint32_t cullMode = 0; cullMode <= 3; ++cullMode) + // default objectshaders: + // We don't wait for these here, because then it can slow down the init time a lot + // We will wait for these to complete in RenderMeshes() just before they will be first used + wi::jobsystem::Wait(object_pso_job_ctx[renderPass][mesh_shader]); + object_pso_job_ctx[renderPass][mesh_shader].priority = wi::jobsystem::Priority::Low; + for (uint32_t shaderType = 0; shaderType < MaterialComponent::SHADERTYPE_COUNT; ++shaderType) + { + wi::jobsystem::Execute(object_pso_job_ctx[renderPass][mesh_shader], [=](wi::jobsystem::JobArgs args) { + for (uint32_t blendMode = 0; blendMode < BLENDMODE_COUNT; ++blendMode) { - for (uint32_t tessellation = 0; tessellation <= 1; ++tessellation) + for (uint32_t cullMode = 0; cullMode <= 3; ++cullMode) { - if (tessellation && renderPass > RENDERPASS_PREPASS_DEPTHONLY) - continue; - for (uint32_t alphatest = 0; alphatest <= 1; ++alphatest) + for (uint32_t tessellation = 0; tessellation <= 1; ++tessellation) { - const bool transparency = blendMode != BLENDMODE_OPAQUE; - if ((renderPass == RENDERPASS_PREPASS || renderPass == RENDERPASS_PREPASS_DEPTHONLY) && transparency) + if (tessellation && renderPass > RENDERPASS_PREPASS_DEPTHONLY) continue; - - SHADERTYPE realVS = GetVSTYPE((RENDERPASS)renderPass, tessellation, alphatest, transparency); - SHADERTYPE realHS = GetHSTYPE((RENDERPASS)renderPass, tessellation, alphatest); - SHADERTYPE realDS = GetDSTYPE((RENDERPASS)renderPass, tessellation, alphatest); - SHADERTYPE realGS = GetGSTYPE((RENDERPASS)renderPass, alphatest, transparency); - SHADERTYPE realPS = GetPSTYPE((RENDERPASS)renderPass, alphatest, transparency, (MaterialComponent::SHADERTYPE)shaderType); - - if (tessellation && (realHS == SHADERTYPE_COUNT || realDS == SHADERTYPE_COUNT)) - continue; - - PipelineStateDesc desc; - desc.vs = realVS < SHADERTYPE_COUNT ? &shaders[realVS] : nullptr; - desc.hs = realHS < SHADERTYPE_COUNT ? &shaders[realHS] : nullptr; - desc.ds = realDS < SHADERTYPE_COUNT ? &shaders[realDS] : nullptr; - desc.gs = realGS < SHADERTYPE_COUNT ? &shaders[realGS] : nullptr; - desc.ps = realPS < SHADERTYPE_COUNT ? &shaders[realPS] : nullptr; - - switch (blendMode) + for (uint32_t alphatest = 0; alphatest <= 1; ++alphatest) { - case BLENDMODE_OPAQUE: - desc.bs = &blendStates[BSTYPE_OPAQUE]; - break; - case BLENDMODE_ALPHA: - desc.bs = &blendStates[BSTYPE_TRANSPARENT]; - break; - case BLENDMODE_ADDITIVE: - desc.bs = &blendStates[BSTYPE_ADDITIVE]; - break; - case BLENDMODE_PREMULTIPLIED: - desc.bs = &blendStates[BSTYPE_PREMULTIPLIED]; - break; - case BLENDMODE_MULTIPLY: - desc.bs = &blendStates[BSTYPE_MULTIPLY]; - break; - default: - assert(0); - break; - } + const bool transparency = blendMode != BLENDMODE_OPAQUE; + if ((renderPass == RENDERPASS_PREPASS || renderPass == RENDERPASS_PREPASS_DEPTHONLY) && transparency) + continue; - switch (renderPass) - { - case RENDERPASS_SHADOW: - desc.bs = &blendStates[transparency ? BSTYPE_TRANSPARENTSHADOW : BSTYPE_COLORWRITEDISABLE]; - break; - case RENDERPASS_RAINBLOCKER: - desc.bs = &blendStates[BSTYPE_COLORWRITEDISABLE]; - break; - default: - break; - } + PipelineStateDesc desc; - switch (renderPass) - { - case RENDERPASS_SHADOW: - desc.dss = &depthStencils[transparency ? DSSTYPE_DEPTHREAD : DSSTYPE_SHADOW]; - break; - case RENDERPASS_MAIN: - if (blendMode == BLENDMODE_ADDITIVE) + if (mesh_shader) { - desc.dss = &depthStencils[DSSTYPE_DEPTHREAD]; + if (tessellation) + continue; + SHADERTYPE realAS = GetASTYPE((RENDERPASS)renderPass, tessellation, alphatest, transparency, mesh_shader); + SHADERTYPE realMS = GetMSTYPE((RENDERPASS)renderPass, tessellation, alphatest, transparency, mesh_shader); + if (realMS == SHADERTYPE_COUNT) + continue; + desc.as = realAS < SHADERTYPE_COUNT ? &shaders[realAS] : nullptr; + desc.ms = realMS < SHADERTYPE_COUNT ? &shaders[realMS] : nullptr; } else { - desc.dss = &depthStencils[transparency ? DSSTYPE_TRANSPARENT : DSSTYPE_DEPTHREADEQUAL]; - } - break; - case RENDERPASS_ENVMAPCAPTURE: - desc.dss = &depthStencils[DSSTYPE_ENVMAP]; - break; - case RENDERPASS_VOXELIZE: - desc.dss = &depthStencils[DSSTYPE_DEPTHDISABLED]; - break; - case RENDERPASS_RAINBLOCKER: - desc.dss = &depthStencils[DSSTYPE_DEFAULT]; - break; - default: - if (blendMode == BLENDMODE_ADDITIVE) - { - desc.dss = &depthStencils[DSSTYPE_DEPTHREAD]; - } - else - { - desc.dss = &depthStencils[DSSTYPE_DEFAULT]; - } - break; - } + SHADERTYPE realVS = GetVSTYPE((RENDERPASS)renderPass, tessellation, alphatest, transparency); + SHADERTYPE realHS = GetHSTYPE((RENDERPASS)renderPass, tessellation, alphatest); + SHADERTYPE realDS = GetDSTYPE((RENDERPASS)renderPass, tessellation, alphatest); + SHADERTYPE realGS = GetGSTYPE((RENDERPASS)renderPass, alphatest, transparency); - switch (renderPass) - { - case RENDERPASS_SHADOW: - desc.rs = &rasterizers[cullMode == (int)CullMode::NONE ? RSTYPE_SHADOW_DOUBLESIDED : RSTYPE_SHADOW]; - break; - case RENDERPASS_VOXELIZE: - desc.rs = &rasterizers[RSTYPE_VOXELIZE]; - break; - default: - switch ((CullMode)cullMode) + if (tessellation && (realHS == SHADERTYPE_COUNT || realDS == SHADERTYPE_COUNT)) + continue; + + desc.vs = realVS < SHADERTYPE_COUNT ? &shaders[realVS] : nullptr; + desc.hs = realHS < SHADERTYPE_COUNT ? &shaders[realHS] : nullptr; + desc.ds = realDS < SHADERTYPE_COUNT ? &shaders[realDS] : nullptr; + desc.gs = realGS < SHADERTYPE_COUNT ? &shaders[realGS] : nullptr; + } + + SHADERTYPE realPS = GetPSTYPE((RENDERPASS)renderPass, alphatest, transparency, (MaterialComponent::SHADERTYPE)shaderType); + desc.ps = realPS < SHADERTYPE_COUNT ? &shaders[realPS] : nullptr; + + switch (blendMode) { + case BLENDMODE_OPAQUE: + desc.bs = &blendStates[BSTYPE_OPAQUE]; + break; + case BLENDMODE_ALPHA: + desc.bs = &blendStates[BSTYPE_TRANSPARENT]; + break; + case BLENDMODE_ADDITIVE: + desc.bs = &blendStates[BSTYPE_ADDITIVE]; + break; + case BLENDMODE_PREMULTIPLIED: + desc.bs = &blendStates[BSTYPE_PREMULTIPLIED]; + break; + case BLENDMODE_MULTIPLY: + desc.bs = &blendStates[BSTYPE_MULTIPLY]; + break; default: - case CullMode::BACK: - desc.rs = &rasterizers[RSTYPE_FRONT]; - break; - case CullMode::NONE: - desc.rs = &rasterizers[RSTYPE_DOUBLESIDED]; - break; - case CullMode::FRONT: - desc.rs = &rasterizers[RSTYPE_BACK]; + assert(0); break; } - break; - } - if (tessellation) - { - desc.pt = PrimitiveTopology::PATCHLIST; - } - else - { - desc.pt = PrimitiveTopology::TRIANGLELIST; - } - - ObjectRenderingVariant variant = {}; - variant.bits.renderpass = renderPass; - variant.bits.shadertype = shaderType; - variant.bits.blendmode = blendMode; - variant.bits.cullmode = cullMode; - variant.bits.tessellation = tessellation; - variant.bits.alphatest = alphatest; - variant.bits.sample_count = 1; - - switch (renderPass) - { - case RENDERPASS_MAIN: - case RENDERPASS_PREPASS: - case RENDERPASS_PREPASS_DEPTHONLY: - { - RenderPassInfo renderpass_info; - if (renderPass == RENDERPASS_PREPASS_DEPTHONLY) + switch (renderPass) { - renderpass_info.rt_count = 0; - renderpass_info.rt_formats[0] = Format::UNKNOWN; + case RENDERPASS_SHADOW: + desc.bs = &blendStates[transparency ? BSTYPE_TRANSPARENTSHADOW : BSTYPE_COLORWRITEDISABLE]; + break; + case RENDERPASS_RAINBLOCKER: + desc.bs = &blendStates[BSTYPE_COLORWRITEDISABLE]; + break; + default: + break; + } + + switch (renderPass) + { + case RENDERPASS_SHADOW: + desc.dss = &depthStencils[transparency ? DSSTYPE_DEPTHREAD : DSSTYPE_SHADOW]; + break; + case RENDERPASS_MAIN: + if (blendMode == BLENDMODE_ADDITIVE) + { + desc.dss = &depthStencils[DSSTYPE_DEPTHREAD]; + } + else + { + desc.dss = &depthStencils[transparency ? DSSTYPE_TRANSPARENT : DSSTYPE_DEPTHREADEQUAL]; + } + break; + case RENDERPASS_ENVMAPCAPTURE: + desc.dss = &depthStencils[DSSTYPE_ENVMAP]; + break; + case RENDERPASS_VOXELIZE: + desc.dss = &depthStencils[DSSTYPE_DEPTHDISABLED]; + break; + case RENDERPASS_RAINBLOCKER: + desc.dss = &depthStencils[DSSTYPE_DEFAULT]; + break; + default: + if (blendMode == BLENDMODE_ADDITIVE) + { + desc.dss = &depthStencils[DSSTYPE_DEPTHREAD]; + } + else + { + desc.dss = &depthStencils[DSSTYPE_DEFAULT]; + } + break; + } + + switch (renderPass) + { + case RENDERPASS_SHADOW: + desc.rs = &rasterizers[cullMode == (int)CullMode::NONE ? RSTYPE_SHADOW_DOUBLESIDED : RSTYPE_SHADOW]; + break; + case RENDERPASS_VOXELIZE: + desc.rs = &rasterizers[RSTYPE_VOXELIZE]; + break; + default: + switch ((CullMode)cullMode) + { + default: + case CullMode::BACK: + desc.rs = &rasterizers[RSTYPE_FRONT]; + break; + case CullMode::NONE: + desc.rs = &rasterizers[RSTYPE_DOUBLESIDED]; + break; + case CullMode::FRONT: + desc.rs = &rasterizers[RSTYPE_BACK]; + break; + } + break; + } + + if (tessellation) + { + desc.pt = PrimitiveTopology::PATCHLIST; } else { - renderpass_info.rt_count = 1; - renderpass_info.rt_formats[0] = renderPass == RENDERPASS_MAIN ? format_rendertarget_main : format_idbuffer; + desc.pt = PrimitiveTopology::TRIANGLELIST; } - renderpass_info.ds_format = format_depthbuffer_main; - const uint32_t msaa_support[] = { 1,2,4,8 }; - for (uint32_t msaa : msaa_support) + + ObjectRenderingVariant variant = {}; + variant.bits.renderpass = renderPass; + variant.bits.shadertype = shaderType; + variant.bits.blendmode = blendMode; + variant.bits.cullmode = cullMode; + variant.bits.tessellation = tessellation; + variant.bits.alphatest = alphatest; + variant.bits.sample_count = 1; + variant.bits.mesh_shader = mesh_shader; + + switch (renderPass) { - variant.bits.sample_count = msaa; - renderpass_info.sample_count = msaa; - device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); - } - } - break; - - case RENDERPASS_ENVMAPCAPTURE: - { - RenderPassInfo renderpass_info; - renderpass_info.rt_count = 1; - renderpass_info.rt_formats[0] = format_rendertarget_envprobe; - renderpass_info.ds_format = format_depthbuffer_envprobe; - const uint32_t msaa_support[] = { 1,8 }; - for (uint32_t msaa : msaa_support) + case RENDERPASS_MAIN: + case RENDERPASS_PREPASS: + case RENDERPASS_PREPASS_DEPTHONLY: { - variant.bits.sample_count = msaa; - renderpass_info.sample_count = msaa; - device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); + RenderPassInfo renderpass_info; + if (renderPass == RENDERPASS_PREPASS_DEPTHONLY) + { + renderpass_info.rt_count = 0; + renderpass_info.rt_formats[0] = Format::UNKNOWN; + } + else + { + renderpass_info.rt_count = 1; + renderpass_info.rt_formats[0] = renderPass == RENDERPASS_MAIN ? format_rendertarget_main : format_idbuffer; + } + renderpass_info.ds_format = format_depthbuffer_main; + const uint32_t msaa_support[] = { 1,2,4,8 }; + for (uint32_t msaa : msaa_support) + { + variant.bits.sample_count = msaa; + renderpass_info.sample_count = msaa; + device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); + } } - } - break; - - case RENDERPASS_SHADOW: - { - RenderPassInfo renderpass_info; - renderpass_info.rt_count = 1; - renderpass_info.rt_formats[0] = format_rendertarget_shadowmap; - renderpass_info.ds_format = format_depthbuffer_shadowmap; - device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); - } - break; - - case RENDERPASS_RAINBLOCKER: - { - RenderPassInfo renderpass_info; - renderpass_info.rt_count = 0; - renderpass_info.ds_format = format_depthbuffer_shadowmap; - device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); - } - break; - - default: - device->CreatePipelineState(&desc, GetObjectPSO(variant)); break; - } + case RENDERPASS_ENVMAPCAPTURE: + { + RenderPassInfo renderpass_info; + renderpass_info.rt_count = 1; + renderpass_info.rt_formats[0] = format_rendertarget_envprobe; + renderpass_info.ds_format = format_depthbuffer_envprobe; + const uint32_t msaa_support[] = { 1,8 }; + for (uint32_t msaa : msaa_support) + { + variant.bits.sample_count = msaa; + renderpass_info.sample_count = msaa; + device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); + } + } + break; + + case RENDERPASS_SHADOW: + { + RenderPassInfo renderpass_info; + renderpass_info.rt_count = 1; + renderpass_info.rt_formats[0] = format_rendertarget_shadowmap; + renderpass_info.ds_format = format_depthbuffer_shadowmap; + device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); + } + break; + + case RENDERPASS_RAINBLOCKER: + { + RenderPassInfo renderpass_info; + renderpass_info.rt_count = 0; + renderpass_info.ds_format = format_depthbuffer_shadowmap; + device->CreatePipelineState(&desc, GetObjectPSO(variant), &renderpass_info); + } + break; + + default: + device->CreatePipelineState(&desc, GetObjectPSO(variant)); + break; + } + } } } } - } - }); + }); + } } } } +bool IsPipelineCreationActive() +{ + for (uint32_t renderPass = 0; renderPass < RENDERPASS_COUNT; ++renderPass) + { + for (uint32_t mesh_shader = 0; mesh_shader <= (IsMeshShaderAllowed() ? 1u : 0u); ++mesh_shader) + { + if (wi::jobsystem::IsBusy(object_pso_job_ctx[renderPass][mesh_shader])) + { + return true; + } + } + } + return false; +} void LoadBuffers() { GPUBufferDesc bd; @@ -2756,7 +2881,7 @@ void Workaround(const int bug , CommandList cmd) //PE: https://github.com/turanszkij/WickedEngine/issues/450#issuecomment-1143647323 //PE: We MUST use RENDERPASS_VOXELIZE (DSSTYPE_DEPTHDISABLED) or it will not work ? - wi::jobsystem::Wait(object_pso_job_ctx[RENDERPASS_VOXELIZE]); + wi::jobsystem::Wait(object_pso_job_ctx[RENDERPASS_VOXELIZE][OBJECT_MESH_SHADER_PSO_DISABLED]); ObjectRenderingVariant variant = {}; variant.bits.renderpass = RENDERPASS_VOXELIZE; variant.bits.blendmode = BLENDMODE_OPAQUE; @@ -2788,7 +2913,9 @@ void RenderMeshes( device->EventBegin("RenderMeshes", cmd); - wi::jobsystem::Wait(object_pso_job_ctx[renderPass]); + // Always wait for non-mesh shader variants, it can be used when mesh shader is not applicable for an object: + wi::jobsystem::Wait(object_pso_job_ctx[renderPass][OBJECT_MESH_SHADER_PSO_DISABLED]); + RenderPassInfo renderpass_info = device->GetRenderPassInfo(cmd); const bool tessellation = @@ -2805,6 +2932,15 @@ void RenderMeshes( const bool shadowRendering = renderPass == RENDERPASS_SHADOW; + const bool mesh_shader = IsMeshShaderAllowed() && + (renderPass == RENDERPASS_PREPASS || renderPass == RENDERPASS_PREPASS_DEPTHONLY || renderPass == RENDERPASS_MAIN || renderPass == RENDERPASS_SHADOW || renderPass == RENDERPASS_RAINBLOCKER); + + if (mesh_shader) + { + // Mesh shader is optional, only wait for these completions if enabled: + wi::jobsystem::Wait(object_pso_job_ctx[renderPass][OBJECT_MESH_SHADER_PSO_ENABLED]); + } + // Pre-allocate space for all the instances in GPU-buffer: const size_t alloc_size = renderQueue.size() * camera_count * sizeof(ShaderMeshInstancePointer); const GraphicsDevice::GPUAllocation instances = device->AllocateGPU(alloc_size, cmd); @@ -2842,6 +2978,7 @@ void RenderMeshes( const float tessF = mesh.GetTessellationFactor(); const bool tessellatorRequested = tessF > 0 && tessellation; + const bool meshShaderRequested = !tessellatorRequested && mesh_shader && mesh.vb_clu.IsValid(); if (forwardLightmaskRequest) { @@ -2882,7 +3019,14 @@ void RenderMeshes( switch (renderPass) { case RENDERPASS_MAIN: - pso = tessellatorRequested ? &PSO_object_wire_tessellation : &PSO_object_wire; + if (meshShaderRequested) + { + pso = &PSO_object_wire_mesh_shader; + } + else + { + pso = tessellatorRequested ? &PSO_object_wire_tessellation : &PSO_object_wire; + } } } else if (material.customShaderID >= 0 && material.customShaderID < (int)customShaders.size()) @@ -2903,6 +3047,7 @@ void RenderMeshes( variant.bits.tessellation = tessellatorRequested; variant.bits.alphatest = material.IsAlphaTestEnabled() || forceAlphaTestForDithering; variant.bits.sample_count = renderpass_info.sample_count; + variant.bits.mesh_shader = meshShaderRequested; pso = GetObjectPSO(variant); assert(pso->IsValid()); @@ -2929,7 +3074,7 @@ void RenderMeshes( device->BindStencilRef(stencilRef, cmd); } - if (prev_ib != &mesh.generalBuffer) + if (!meshShaderRequested && prev_ib != &mesh.generalBuffer) { device->BindIndexBuffer(&mesh.generalBuffer, mesh.GetIndexFormat(), mesh.ib.offset, cmd); prev_ib = &mesh.generalBuffer; @@ -2955,12 +3100,26 @@ void RenderMeshes( { device->BindPipelineState(pso_backside, cmd); device->PushConstants(&push, sizeof(push), cmd); - device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); + if (meshShaderRequested) + { + device->DispatchMesh((mesh.cluster_ranges[subsetIndex].clusterCount + 31) / 32, instancedBatch.instanceCount, 1, cmd); + } + else + { + device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); + } } device->BindPipelineState(pso, cmd); device->PushConstants(&push, sizeof(push), cmd); - device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); + if (meshShaderRequested) + { + device->DispatchMesh((mesh.cluster_ranges[subsetIndex].clusterCount + 31) / 32, instancedBatch.instanceCount, 1, cmd); + } + else + { + device->DrawIndexedInstanced(subset.indexCount, instancedBatch.instanceCount, subset.indexOffset, 0, 0, cmd); + } } }; @@ -6035,6 +6194,14 @@ void DrawShadowmaps( { XMStoreFloat4x4(&cb.cameras[cascade].view_projection, shcams[cascade].view_projection); cb.cameras[cascade].output_index = cascade; + for (int i = 0; i < arraysize(cb.cameras[cascade].frustum.planes); ++i) + { + cb.cameras[cascade].frustum.planes[i] = shcams[cascade].frustum.planes[i]; + } + cb.cameras[cascade].options = SHADERCAMERA_OPTION_ORTHO; + cb.cameras[cascade].forward.x = -light.direction.x; + cb.cameras[cascade].forward.y = -light.direction.y; + cb.cameras[cascade].forward.z = -light.direction.z; Viewport& vp = viewports[cascade]; vp.top_left_x = float(shadow_rect.x + cascade * shadow_rect.w); @@ -6140,6 +6307,10 @@ void DrawShadowmaps( XMStoreFloat4x4(&cb.cameras[0].view_projection, shcam.view_projection); cb.cameras[0].output_index = 0; + for (int i = 0; i < arraysize(cb.cameras[0].frustum.planes); ++i) + { + cb.cameras[0].frustum.planes[i] = shcam.frustum.planes[i]; + } device->BindDynamicConstantBuffer(cb, CBSLOT_RENDERER_CAMERA, cmd); Viewport vp; @@ -6171,6 +6342,7 @@ void DrawShadowmaps( if (!vis.visibleHairs.empty()) { cb.cameras[0].position = vis.camera->Eye; + cb.cameras[0].options = SHADERCAMERA_OPTION_NONE; XMStoreFloat4x4(&cb.cameras[0].view_projection, shcam.view_projection); device->BindDynamicConstantBuffer(cb, CBSLOT_RENDERER_CAMERA, cmd); @@ -6239,6 +6411,11 @@ void DrawShadowmaps( // - there will be only as many cameras, as many cubemap face frustums are visible from main camera // - output_index is mapping camera to viewport, used by shader to output to SV_ViewportArrayIndex cb.cameras[camera_count].output_index = shcam; + cb.cameras[camera_count].options = SHADERCAMERA_OPTION_NONE; + for (int i = 0; i < arraysize(cb.cameras[camera_count].frustum.planes); ++i) + { + cb.cameras[camera_count].frustum.planes[i] = cameras[shcam].frustum.planes[i]; + } frusta[camera_count] = cameras[shcam].frustum; camera_count++; } @@ -6380,6 +6557,10 @@ void DrawShadowmaps( const uint cascade = 0; XMStoreFloat4x4(&cb.cameras[cascade].view_projection, shcam.view_projection); cb.cameras[cascade].output_index = cascade; + for (int i = 0; i < arraysize(cb.cameras[cascade].frustum.planes); ++i) + { + cb.cameras[cascade].frustum.planes[i] = shcam.frustum.planes[i]; + } Viewport vp; vp.top_left_x = float(vis.rain_blocker_shadow_rect.x + cascade * vis.rain_blocker_shadow_rect.w); @@ -10300,6 +10481,7 @@ void BindCameraCB( shadercam.texture_depth_index_prev = camera_previous.texture_depth_index; shadercam.texture_vxgi_diffuse_index = camera.texture_vxgi_diffuse_index; shadercam.texture_vxgi_specular_index = camera.texture_vxgi_specular_index; + shadercam.texture_reprojected_depth_index = camera.texture_reprojected_depth_index; device->BindDynamicConstantBuffer(cb, CBSLOT_RENDERER_CAMERA, cmd); } @@ -17531,6 +17713,109 @@ void CopyDepthStencil( } +void ComputeReprojectedDepthPyramid( + const Texture& input_depth, + const Texture& input_velocity, + const Texture& output_depth_pyramid, + CommandList cmd +) +{ + device->EventBegin("ComputeReprojectedDepthPyramid", cmd); + + BindCommonResources(cmd); + + GPUResource empty; + const GPUResource* unbind[] = { + &empty, + &empty, + &empty, + &empty, + }; + + device->BindComputeShader(&shaders[CSTYPE_DEPTH_REPROJECT], cmd); + + const TextureDesc& input_desc = input_depth.GetDesc(); + const TextureDesc& output_desc = output_depth_pyramid.GetDesc(); + + PostProcess postprocess = {}; + postprocess.resolution.x = output_desc.width; + postprocess.resolution.y = output_desc.height; + postprocess.resolution_rcp.x = 1.0f / postprocess.resolution.x; + postprocess.resolution_rcp.y = 1.0f / postprocess.resolution.y; + device->PushConstants(&postprocess, sizeof(postprocess), cmd); + + { + GPUBarrier barriers[] = { + GPUBarrier::Image(&output_depth_pyramid, output_depth_pyramid.desc.layout, ResourceState::UNORDERED_ACCESS), + }; + device->Barrier(barriers, arraysize(barriers), cmd); + } + + device->ClearUAV(&output_depth_pyramid, 0, cmd); + device->Barrier(GPUBarrier::Memory(&output_depth_pyramid), cmd); + + device->BindResource(&input_depth, 0, cmd); + device->BindResource(&input_velocity, 1, cmd); + + device->BindUAVs(unbind, 0, arraysize(unbind), cmd); + + device->BindUAV(&output_depth_pyramid, 0, cmd, 0); + PushBarrier(GPUBarrier::Image(&output_depth_pyramid, ResourceState::UNORDERED_ACCESS, output_depth_pyramid.desc.layout, 0)); + + if (output_desc.mip_levels > 1) + { + device->BindUAV(&output_depth_pyramid, 1, cmd, 1); + PushBarrier(GPUBarrier::Image(&output_depth_pyramid, ResourceState::UNORDERED_ACCESS, output_depth_pyramid.desc.layout, 1)); + } + + device->Dispatch( + (postprocess.resolution.x + 7 - 1) / 8, + (postprocess.resolution.y + 7 - 1) / 8, + 1, + cmd + ); + + FlushBarriers(cmd); + + device->BindComputeShader(&shaders[CSTYPE_DEPTH_PYRAMID], cmd); + + uint bottom = 2; + while (output_desc.mip_levels > bottom) + { + device->BindUAVs(unbind, 0, arraysize(unbind), cmd); + postprocess.resolution.x = std::max(1u, output_desc.width >> bottom); + postprocess.resolution.y = std::max(1u, output_desc.height >> bottom); + postprocess.resolution_rcp.x = 1.0f / postprocess.resolution.x; + postprocess.resolution_rcp.y = 1.0f / postprocess.resolution.y; + device->PushConstants(&postprocess, sizeof(postprocess), cmd); + + device->BindResource(&output_depth_pyramid, 0, cmd, bottom - 1); + + device->BindUAV(&output_depth_pyramid, 0, cmd, bottom); + PushBarrier(GPUBarrier::Image(&output_depth_pyramid, ResourceState::UNORDERED_ACCESS, output_depth_pyramid.desc.layout, bottom)); + + if (output_desc.mip_levels > (bottom + 1)) + { + device->BindUAV(&output_depth_pyramid, 1, cmd, bottom + 1); + PushBarrier(GPUBarrier::Image(&output_depth_pyramid, ResourceState::UNORDERED_ACCESS, output_depth_pyramid.desc.layout, bottom + 1)); + } + + device->Dispatch( + (postprocess.resolution.x + 7 - 1) / 8, + (postprocess.resolution.y + 7 - 1) / 8, + 1, + cmd + ); + + FlushBarriers(cmd); + + bottom += 2; + } + + device->EventEnd(cmd); +} + + Ray GetPickRay(long cursorX, long cursorY, const wi::Canvas& canvas, const CameraComponent& camera) { float screenW = canvas.GetLogicalWidth(); @@ -17847,6 +18132,22 @@ float GetGIBoost() { return GI_BOOST; } +void SetMeshShaderAllowed(bool value) +{ + MESH_SHADER_ALLOWED = value; +} +bool IsMeshShaderAllowed() +{ + return MESH_SHADER_ALLOWED && device->CheckCapability(GraphicsDeviceCapability::MESH_SHADER); +} +void SetMeshletOcclusionCullingEnabled(bool value) +{ + MESHLET_OCCLUSION_CULLING = value; +} +bool IsMeshletOcclusionCullingEnabled() +{ + return MESHLET_OCCLUSION_CULLING; +} wi::Resource CreatePaintableTexture(uint32_t width, uint32_t height, uint32_t mips, wi::Color initialColor) { diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index 155b5258f..d8bdcdaac 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -99,6 +99,8 @@ namespace wi::renderer const wi::vector& permutation_defines = {} ); + // Whether background pipeline compilations are active + bool IsPipelineCreationActive(); struct Visibility { @@ -979,6 +981,13 @@ namespace wi::renderer void OcclusionCulling_Render(const wi::scene::CameraComponent& camera, const Visibility& vis, wi::graphics::CommandList cmd); void OcclusionCulling_Resolve(const Visibility& vis, wi::graphics::CommandList cmd); + void ComputeReprojectedDepthPyramid( + const wi::graphics::Texture& input_depth, + const wi::graphics::Texture& input_velocity, + const wi::graphics::Texture& output_depth_pyramid, + wi::graphics::CommandList cmd + ); + enum MIPGENFILTER { MIPGENFILTER_POINT, @@ -1097,6 +1106,10 @@ namespace wi::renderer float GetDDGIBlendSpeed(); void SetGIBoost(float value); float GetGIBoost(); + void SetMeshShaderAllowed(bool value); + bool IsMeshShaderAllowed(); + void SetMeshletOcclusionCullingEnabled(bool value); + bool IsMeshletOcclusionCullingEnabled(); void Workaround( const int bug, wi::graphics::CommandList cmd); // Gets pick ray according to the current screen resolution and pointer coordinates. Can be used as input into RayIntersectWorld() diff --git a/WickedEngine/wiRenderer_BindLua.cpp b/WickedEngine/wiRenderer_BindLua.cpp index bdb4242b5..7a62aba4b 100644 --- a/WickedEngine/wiRenderer_BindLua.cpp +++ b/WickedEngine/wiRenderer_BindLua.cpp @@ -225,6 +225,32 @@ namespace wi::lua::renderer } return 0; } + int SetMeshShaderAllowed(lua_State* L) + { + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + wi::renderer::SetMeshShaderAllowed(wi::lua::SGetBool(L, 1)); + } + else + { + wi::lua::SError(L, "SetMeshShaderAllowed(bool enabled) not enough arguments!"); + } + return 0; + } + int SetMeshletOcclusionCullingEnabled(lua_State* L) + { + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + wi::renderer::SetMeshletOcclusionCullingEnabled(wi::lua::SGetBool(L, 1)); + } + else + { + wi::lua::SError(L, "SetMeshletOcclusionCullingEnabled(bool enabled) not enough arguments!"); + } + return 0; + } int DrawLine(lua_State* L) { @@ -809,6 +835,8 @@ namespace wi::lua::renderer wi::lua::RegisterFunc("SetResolution", SetResolution); wi::lua::RegisterFunc("SetDebugLightCulling", SetDebugLightCulling); wi::lua::RegisterFunc("SetOcclusionCullingEnabled", SetOcclusionCullingEnabled); + wi::lua::RegisterFunc("SetMeshShaderAllowed", SetMeshShaderAllowed); + wi::lua::RegisterFunc("SetMeshletOcclusionCullingEnabled", SetMeshletOcclusionCullingEnabled); wi::lua::RegisterFunc("DrawLine", DrawLine); wi::lua::RegisterFunc("DrawPoint", DrawPoint); diff --git a/WickedEngine/wiScene.cpp b/WickedEngine/wiScene.cpp index a346119ae..8a71af536 100644 --- a/WickedEngine/wiScene.cpp +++ b/WickedEngine/wiScene.cpp @@ -3893,6 +3893,11 @@ namespace wi::scene geometry.vb_uvs = mesh.vb_uvs.descriptor_srv; geometry.vb_atl = mesh.vb_atl.descriptor_srv; geometry.vb_pre = mesh.so_pre.descriptor_srv; + if (wi::renderer::IsMeshShaderAllowed()) + { + geometry.vb_clu = mesh.vb_clu.descriptor_srv; + geometry.vb_bou = mesh.vb_bou.descriptor_srv; + } geometry.aabb_min = mesh.aabb._min; geometry.aabb_max = mesh.aabb._max; geometry.tessellation_factor = mesh.tessellationFactor; @@ -3925,13 +3930,26 @@ namespace wi::scene subset.materialIndex = 0; } - geometry.indexOffset = subset.indexOffset; - geometry.indexCount = subset.indexCount; - geometry.materialIndex = subset.materialIndex; - geometry.meshletOffset = mesh.meshletCount; - geometry.meshletCount = triangle_count_to_meshlet_count(subset.indexCount / 3u); - mesh.meshletCount += geometry.meshletCount; - std::memcpy(geometryArrayMapped + mesh.geometryOffset + subsetIndex, &geometry, sizeof(geometry)); + ShaderGeometry subsetGeometry = geometry; + subsetGeometry.indexOffset = subset.indexOffset; + subsetGeometry.indexCount = subset.indexCount; + subsetGeometry.materialIndex = subset.materialIndex; + if (wi::renderer::IsMeshShaderAllowed() && subsetIndex < mesh.cluster_ranges.size()) + { + subsetGeometry.meshletOffset = mesh.cluster_ranges[subsetIndex].clusterOffset; + subsetGeometry.meshletCount = mesh.cluster_ranges[subsetIndex].clusterCount; + } + else + { + subsetGeometry.meshletOffset = mesh.meshletCount; + subsetGeometry.meshletCount = triangle_count_to_meshlet_count(subset.indexCount / 3u); + } + mesh.meshletCount += subsetGeometry.meshletCount; + if (material != nullptr && material->IsDoubleSided()) + { + subsetGeometry.flags |= SHADERMESH_FLAG_DOUBLE_SIDED; + } + std::memcpy(geometryArrayMapped + mesh.geometryOffset + subsetIndex, &subsetGeometry, sizeof(subsetGeometry)); subsetIndex++; } } @@ -4423,6 +4441,7 @@ namespace wi::scene XMStoreFloat4x4(matrix_objects.data() + args.jobIndex, W); XMFLOAT4X4 worldMatrix = matrix_objects[args.jobIndex]; + inst.transformRaw.Create(worldMatrix); if (IsFormatUnorm(mesh.position_format) && !mesh.so_pos.IsValid()) { // The UNORM correction is only done for the GPU data! diff --git a/WickedEngine/wiScene_BindLua.cpp b/WickedEngine/wiScene_BindLua.cpp index 7a00ecd93..8d6df08e2 100644 --- a/WickedEngine/wiScene_BindLua.cpp +++ b/WickedEngine/wiScene_BindLua.cpp @@ -4776,25 +4776,7 @@ int MeshComponent_BindLua::GetMeshSubsetMaterialID(lua_State* L) } int MeshComponent_BindLua::CreateSubset(lua_State* L) { - int ret = 0; - const uint32_t lod_count = component->GetLODCount(); - for (uint32_t lod = 0; lod < lod_count; ++lod) - { - uint32_t first_subset = 0; - uint32_t last_subset = 0; - component->GetLODSubsetRange(lod, first_subset, last_subset); - MeshComponent::MeshSubset subset = component->subsets[last_subset]; - component->subsets.insert(component->subsets.begin() + last_subset, subset); - if (lod == 0) - { - ret = last_subset; - } - } - if (lod_count > 0) - { - component->subsets_per_lod++; - } - wi::lua::SSetInt(L, ret); + wi::lua::SSetInt(L, (int)component->CreateSubset()); return 1; } diff --git a/WickedEngine/wiScene_Components.cpp b/WickedEngine/wiScene_Components.cpp index 098a19c67..e40d52e13 100644 --- a/WickedEngine/wiScene_Components.cpp +++ b/WickedEngine/wiScene_Components.cpp @@ -829,6 +829,112 @@ namespace wi::scene } } + wi::vector clusters; + wi::vector cluster_bounds; + + if (device->CheckCapability(GraphicsDeviceCapability::MESH_SHADER)) + { + const size_t max_vertices = MESHLET_VERTEX_COUNT; + const size_t max_triangles = MESHLET_TRIANGLE_COUNT; + const float cone_weight = 0.5f; + + const uint32_t lod_count = GetLODCount(); + for (uint32_t lod = 0; lod < lod_count; ++lod) + { + uint32_t first_subset = 0; + uint32_t last_subset = 0; + GetLODSubsetRange(lod, first_subset, last_subset); + for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) + { + const MeshSubset& subset = subsets[subsetIndex]; + + size_t max_meshlets = meshopt_buildMeshletsBound(subset.indexCount, max_vertices, max_triangles); + std::vector meshopt_meshlets(max_meshlets); + std::vector meshlet_vertices(max_meshlets * max_vertices); + std::vector meshlet_triangles(max_meshlets * max_triangles * 3); + + size_t meshlet_count = meshopt_buildMeshlets( + meshopt_meshlets.data(), + meshlet_vertices.data(), + meshlet_triangles.data(), + &indices[subset.indexOffset], + subset.indexCount, + &vertex_positions[0].x, + vertex_positions.size(), + sizeof(XMFLOAT3), + max_vertices, + max_triangles, + cone_weight + ); + + clusters.reserve(clusters.size() + meshlet_count); + cluster_bounds.reserve(cluster_bounds.size() + meshlet_count); + + SubsetClusterRange& meshlet_range = cluster_ranges.emplace_back(); + meshlet_range.clusterOffset = (uint32_t)clusters.size(); + meshlet_range.clusterCount = (uint32_t)meshlet_count; + + const meshopt_Meshlet& last = meshopt_meshlets[meshlet_count - 1]; + + meshlet_vertices.resize(last.vertex_offset + last.vertex_count); + meshlet_triangles.resize(last.triangle_offset + ((last.triangle_count * 3 + 3) & ~3)); + meshopt_meshlets.resize(meshlet_count); + + for (size_t i = 0; i < meshopt_meshlets.size(); ++i) + { + const meshopt_Meshlet& meshlet = meshopt_meshlets[i]; + meshopt_optimizeMeshlet( + &meshlet_vertices[meshlet.vertex_offset], + &meshlet_triangles[meshlet.triangle_offset], + meshlet.triangle_count, + meshlet.vertex_count + ); + + meshopt_Bounds bounds = meshopt_computeMeshletBounds( + &meshlet_vertices[meshlet.vertex_offset], + &meshlet_triangles[meshlet.triangle_offset], + meshlet.triangle_count, + &vertex_positions[0].x, + vertex_positions.size(), + sizeof(XMFLOAT3) + ); + + ShaderClusterBounds& clusterbound = cluster_bounds.emplace_back(); + clusterbound.sphere.center.x = bounds.center[0]; + clusterbound.sphere.center.y = bounds.center[1]; + clusterbound.sphere.center.z = bounds.center[2]; + clusterbound.sphere.radius = bounds.radius; + clusterbound.cone_axis.x = -bounds.cone_axis[0]; + clusterbound.cone_axis.y = -bounds.cone_axis[1]; + clusterbound.cone_axis.z = -bounds.cone_axis[2]; + clusterbound.cone_cutoff = bounds.cone_cutoff; + + ShaderCluster& cluster = clusters.emplace_back(); + cluster.vertexCount = meshlet.vertex_count; + cluster.triangleCount = meshlet.triangle_count; + for (size_t tri = 0; tri < meshlet.triangle_count; ++tri) + { + cluster.triangles[tri].init( + meshlet_triangles[meshlet.triangle_offset + tri * 3 + 0], + meshlet_triangles[meshlet.triangle_offset + tri * 3 + 1], + meshlet_triangles[meshlet.triangle_offset + tri * 3 + 2] + ); + } + for (size_t vert = 0; vert < meshlet.vertex_count; ++vert) + { + cluster.vertices[vert] = meshlet_vertices[meshlet.vertex_offset + vert]; + } + } + } + } + + bd.size = AlignTo(bd.size, sizeof(ShaderCluster)); + bd.size = AlignTo(bd.size + clusters.size() * sizeof(ShaderCluster), alignment); + + bd.size = AlignTo(bd.size, sizeof(ShaderClusterBounds)); + bd.size = AlignTo(bd.size + cluster_bounds.size() * sizeof(ShaderClusterBounds), alignment); + } + auto init_callback = [&](void* dest) { uint8_t* buffer_data = (uint8_t*)dest; uint64_t buffer_offset = 0ull; @@ -1112,6 +1218,23 @@ namespace wi::scene } vb_mor.size = buffer_offset - vb_mor.offset; } + + if (!clusters.empty()) + { + buffer_offset = AlignTo(buffer_offset, sizeof(ShaderCluster)); + vb_clu.offset = buffer_offset; + vb_clu.size = clusters.size() * sizeof(ShaderCluster); + std::memcpy(buffer_data + buffer_offset, clusters.data(), vb_clu.size); + buffer_offset += AlignTo(vb_clu.size, alignment); + } + if (!cluster_bounds.empty()) + { + buffer_offset = AlignTo(buffer_offset, sizeof(ShaderClusterBounds)); + vb_bou.offset = buffer_offset; + vb_bou.size = cluster_bounds.size() * sizeof(ShaderClusterBounds); + std::memcpy(buffer_data + buffer_offset, cluster_bounds.data(), vb_bou.size); + buffer_offset += AlignTo(vb_bou.size, alignment); + } }; bool success = device->CreateBuffer2(&bd, init_callback, &generalBuffer); @@ -1162,6 +1285,18 @@ namespace wi::scene vb_mor.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_mor.offset, vb_mor.size, &morph_format); vb_mor.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_mor.subresource_srv); } + if (vb_clu.IsValid()) + { + static constexpr uint32_t cluster_stride = sizeof(ShaderCluster); + vb_clu.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_clu.offset, vb_clu.size, nullptr, &cluster_stride); + vb_clu.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_clu.subresource_srv); + } + if (vb_bou.IsValid()) + { + static constexpr uint32_t cluster_stride = sizeof(ShaderClusterBounds); + vb_bou.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_bou.offset, vb_bou.size, nullptr, &cluster_stride); + vb_bou.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_bou.subresource_srv); + } if (!vertex_boneindices.empty() || !morph_targets.empty()) { @@ -1736,6 +1871,45 @@ namespace wi::scene bvh.allocation.capacity() + bvh_leaf_aabbs.size() * sizeof(wi::primitive::AABB); } + size_t MeshComponent::GetClusterCount() const + { + size_t cnt = 0; + for (auto& x : cluster_ranges) + { + cnt = std::max(cnt, size_t(x.clusterOffset + x.clusterCount)); + } + return cnt; + } + size_t MeshComponent::CreateSubset() + { + int ret = 0; + const uint32_t lod_count = GetLODCount(); + for (uint32_t lod = 0; lod < lod_count; ++lod) + { + uint32_t first_subset = 0; + uint32_t last_subset = 0; + GetLODSubsetRange(lod, first_subset, last_subset); + MeshComponent::MeshSubset subset; + subset.indexOffset = ~0u; + subset.indexCount = 0; + for (uint32_t subsetIndex = first_subset; subsetIndex < last_subset; ++subsetIndex) + { + subset.indexOffset = std::min(subset.indexOffset, subsets[subsetIndex].indexOffset); + subset.indexCount = std::max(subset.indexCount, subsets[subsetIndex].indexOffset + subsets[subsetIndex].indexCount); + } + subsets.insert(subsets.begin() + last_subset, subset); + if (lod == 0) + { + ret = last_subset; + } + } + if (lod_count > 0) + { + subsets_per_lod++; + } + CreateRenderData(); // mesh shader needs to rebuild clusters, otherwise wouldn't be needed + return ret; + } void ObjectComponent::ClearLightmap() { diff --git a/WickedEngine/wiScene_Components.h b/WickedEngine/wiScene_Components.h index cb84f90f4..67f682a00 100644 --- a/WickedEngine/wiScene_Components.h +++ b/WickedEngine/wiScene_Components.h @@ -439,6 +439,8 @@ namespace wi::scene BufferView vb_col; BufferView vb_bon; BufferView vb_mor; + BufferView vb_clu; + BufferView vb_bou; BufferView so_pos; BufferView so_nor; BufferView so_tan; @@ -462,6 +464,13 @@ namespace wi::scene wi::vector bvh_leaf_aabbs; wi::BVH bvh; + struct SubsetClusterRange + { + uint32_t clusterOffset = 0; + uint32_t clusterCount = 0; + }; + wi::vector cluster_ranges; + inline void SetRenderable(bool value) { if (value) { _flags |= RENDERABLE; } else { _flags &= ~RENDERABLE; } } inline void SetDoubleSided(bool value) { if (value) { _flags |= DOUBLE_SIDED; } else { _flags &= ~DOUBLE_SIDED; } } inline void SetDoubleSidedShadow(bool value) { if (value) { _flags |= DOUBLE_SIDED_SHADOW; } else { _flags &= ~DOUBLE_SIDED_SHADOW; } } @@ -500,6 +509,11 @@ namespace wi::scene } } + size_t GetClusterCount() const; + + // Creates a new subset as a combination of the subsets of the first LOD, returns its index. This works if there are multiple LODs which are also contained in subsets array + size_t CreateSubset(); + // Deletes all GPU resources void DeleteRenderData(); @@ -1161,6 +1175,7 @@ namespace wi::scene int buffer_entitytiles_index = -1; int texture_vxgi_diffuse_index = -1; int texture_vxgi_specular_index = -1; + int texture_reprojected_depth_index = -1; uint shadercamera_options = SHADERCAMERA_OPTION_NONE; void CreatePerspective(float newWidth, float newHeight, float newNear, float newFar, float newFOV = XM_PI / 3.0f); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 96f71a07f..bce6c7bdc 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 539; + const int revision = 540; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);