diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 53e79bb87..5fab24497 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -5,4 +5,5 @@ add_subdirectory(physics) add_subdirectory(application) add_subdirectory(addons) add_subdirectory(audio) -add_subdirectory(input) \ No newline at end of file +add_subdirectory(input) +add_subdirectory(options) diff --git a/code/addons/dynui/imgui/shaders/imgui.fx b/code/addons/dynui/imgui/shaders/imgui.fx index 6c9645563..f5b62cc3f 100644 --- a/code/addons/dynui/imgui/shaders/imgui.fx +++ b/code/addons/dynui/imgui/shaders/imgui.fx @@ -11,11 +11,15 @@ #include "lib/shared.fxh" #include "lib/defaultsamplers.fxh" + // put variables in push-constant block push constant ImGUI [ string Visibility = "PS|VS"; ] { mat4 TextProjectionModel; + uint ColorMask; uint PackedTextureInfo; + float RangeMin; + float RangeMax; }; group(BATCH_GROUP) sampler_state TextureSampler @@ -37,13 +41,31 @@ render_state TextState //------------------------------------------------------------------------------ /** */ -void UnpackTexture(uint val, out uint id, out uint type, out uint mip, out uint layer, out uint useAlpha) +void +UnpackTexture(uint val, out uint id, out uint type, out uint mip, out uint layer, out uint useRange, out uint useAlpha) +{ + const uint TEXTURE_TYPE_MASK = 0xF; + const uint TEXTURE_LAYER_MASK = 0xFF; + const uint TEXTURE_MIP_MASK = 0xF; + const uint TEXTURE_USE_RANGE_MASK = 0x1; + const uint TEXTURE_USE_ALPHA_MASK = 0x1; + const uint TEXTURE_ID_MASK = 0xFFF; + + type = val & TEXTURE_TYPE_MASK; + layer = (val >> 4) & TEXTURE_LAYER_MASK; + mip = (val >> 12) & TEXTURE_MIP_MASK; + useRange = (val >> 16) & TEXTURE_USE_RANGE_MASK; + useAlpha = (val >> 17) & TEXTURE_USE_ALPHA_MASK; + id = (val >> 18) & TEXTURE_ID_MASK; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec4 +UnpackMask(uint val) { - type = val & 0xF; - layer = (val >> 4) & 0xFF; - mip = (val >> 12) & 0xFF; - useAlpha = (val >> 20) & 0x1; - id = (val >> 21) & 0xFFF; + return vec4(val & 0x1, (val >> 1) & 0x1, (val >> 2) & 0x1, (val >> 3) & 0x1); } //------------------------------------------------------------------------------ @@ -75,8 +97,8 @@ psMain( [color0] out vec4 FinalColor) { vec4 texColor; - uint id, type, layer, mip, useAlpha; - UnpackTexture(ImGUI.PackedTextureInfo, id, type, mip, layer, useAlpha); + uint id, type, layer, mip, useAlpha, useRange; + UnpackTexture(ImGUI.PackedTextureInfo, id, type, mip, layer, useRange, useAlpha); if (type == 0) texColor = sample2DLod(id, TextureSampler, UV, mip); else if (type == 1) @@ -86,12 +108,19 @@ psMain( ivec3 size = textureSize(make_sampler3D(id, TextureSampler), int(mip)); texColor = sample3DLod(id, TextureSampler, vec3(UV, layer / float(size.z)), mip); } + + if (useRange != 0) + { + texColor.rgb = (texColor.rgb - ImGUI.RangeMin) / (ImGUI.RangeMax - ImGUI.RangeMin); + } if (useAlpha == 0) texColor.a = 1; + + vec4 mask = UnpackMask(ImGUI.ColorMask); // Since we are using sRGB output, remember to degamma - FinalColor = Color * texColor; + FinalColor = Color * texColor * mask; } diff --git a/code/addons/dynui/imguicontext.cc b/code/addons/dynui/imguicontext.cc index 2ad9733cb..5cb345003 100644 --- a/code/addons/dynui/imguicontext.cc +++ b/code/addons/dynui/imguicontext.cc @@ -128,11 +128,24 @@ ImguiDrawFunction(const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle< struct TextureInfo { - uint32 type : 4; - uint32 layer : 8; - uint32 mip : 8; - uint32 useAlpha : 1; - uint32 id : 11; + uint type : 4; + uint layer : 8; + uint mip : 4; + uint useRange : 1; + uint useAlpha : 1; + uint id : 11; + }; + + union ColorMask + { + struct + { + uint red: 1; + uint green: 1; + uint blue: 1; + uint alpha: 1; + }; + uint bits = 0xF; }; IndexT vertexOffset = 0; @@ -175,6 +188,7 @@ ImguiDrawFunction(const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle< TextureInfo texInfo; texInfo.type = 0; + texInfo.useRange = tex.useRange; texInfo.useAlpha = tex.useAlpha; // set texture in shader, we shouldn't have to put it into ImGui @@ -199,7 +213,19 @@ ImguiDrawFunction(const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle< texInfo.mip = tex.mip; texInfo.id = CoreGraphics::TextureGetBindlessHandle(texture); + ColorMask colorMask; + colorMask.red = tex.red; + colorMask.green = tex.green; + colorMask.blue = tex.blue; + colorMask.alpha = tex.alpha; + CoreGraphics::CmdPushConstants(cmdBuf, CoreGraphics::GraphicsPipeline, ImguiContext::state.packedTextureInfo, sizeof(TextureInfo), (byte*)& texInfo); + CoreGraphics::CmdPushConstants(cmdBuf, CoreGraphics::GraphicsPipeline, ImguiContext::state.colorMaskConstant, sizeof(ColorMask), (byte*)&colorMask); + if (texInfo.useRange) + { + CoreGraphics::CmdPushConstants(cmdBuf, CoreGraphics::GraphicsPipeline, ImguiContext::state.rangeMinConstant, sizeof(float), &tex.rangeMin); + CoreGraphics::CmdPushConstants(cmdBuf, CoreGraphics::GraphicsPipeline, ImguiContext::state.rangeMaxConstant, sizeof(float), &tex.rangeMax); + } // setup primitive CoreGraphics::PrimitiveGroup primitive; @@ -343,6 +369,9 @@ ImguiContext::Create() state.textProjectionConstant = CoreGraphics::ShaderGetConstantBinding(state.uiShader, "TextProjectionModel"); state.packedTextureInfo = CoreGraphics::ShaderGetConstantBinding(state.uiShader, "PackedTextureInfo"); + state.rangeMinConstant = CoreGraphics::ShaderGetConstantBinding(state.uiShader, "RangeMin"); + state.rangeMaxConstant = CoreGraphics::ShaderGetConstantBinding(state.uiShader, "RangeMax"); + state.colorMaskConstant = CoreGraphics::ShaderGetConstantBinding(state.uiShader, "ColorMask"); state.inputHandler = ImguiInputHandler::Create(); Input::InputServer::Instance()->AttachInputHandler(Input::InputPriority::DynUi, state.inputHandler.upcast()); @@ -353,7 +382,7 @@ ImguiContext::Create() components.Append(VertexComponent(1, VertexComponent::Float2, 0)); components.Append(VertexComponent(2, VertexComponent::UByte4N, 0)); state.vlo = CoreGraphics::CreateVertexLayout({ .name = "ImGui"_atm, .comps = components }); - + #if WITH_NEBULA_EDITOR if (App::GameApplication::IsEditorEnabled()) { @@ -394,6 +423,24 @@ ImguiContext::Create() }); } + FrameScript_default::RegisterSubgraphPipelines_ImGUI_Pass([](const CoreGraphics::PassId pass, uint subpass) + { + CoreGraphics::InputAssemblyKey inputAssembly{ CoreGraphics::PrimitiveTopology::TriangleList, false }; + if (state.editorPipeline != CoreGraphics::InvalidPipelineId) + CoreGraphics::DestroyGraphicsPipeline(state.editorPipeline); + state.pipeline = CoreGraphics::CreateGraphicsPipeline({ state.prog, pass, subpass, inputAssembly }); + }); + + FrameScript_default::RegisterSubgraph_ImGUI_Pass([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { +#ifdef NEBULA_NO_DYNUI_ASSERTS + ImguiContext::RecoverImGuiContextErrors(); +#endif + //ImGui::Render(); + //ImguiDrawFunction(cmdBuf, viewport, false); + }); + + SizeT numBuffers = CoreGraphics::GetNumBufferedFrames(); CoreGraphics::BufferCreateInfo vboInfo; diff --git a/code/addons/dynui/imguicontext.h b/code/addons/dynui/imguicontext.h index 525fc039f..1a23f2285 100644 --- a/code/addons/dynui/imguicontext.h +++ b/code/addons/dynui/imguicontext.h @@ -31,6 +31,13 @@ struct ImguiTextureId uint layer : 8 = 0; uint mip : 4 = 0; uint useAlpha : 1 = 0; + uint useRange : 1 = 0; + float rangeMin, rangeMax; + uint red : 1 = 1; + uint green : 1 = 1; + uint blue : 1 = 1; + uint alpha : 1 = 1; + }; class ImguiContext : public Graphics::GraphicsContext @@ -79,6 +86,9 @@ class ImguiContext : public Graphics::GraphicsContext IndexT textProjectionConstant; IndexT packedTextureInfo; + IndexT rangeMinConstant; + IndexT rangeMaxConstant; + IndexT colorMaskConstant; CoreGraphics::ResourceTableId resourceTable; //Ptr vboBufferLock; //Ptr iboBufferLock; diff --git a/code/addons/graphicsfeature/CMakeLists.txt b/code/addons/graphicsfeature/CMakeLists.txt index 646a0cdf2..49b1cbffd 100644 --- a/code/addons/graphicsfeature/CMakeLists.txt +++ b/code/addons/graphicsfeature/CMakeLists.txt @@ -1,5 +1,4 @@ nebula_begin_module(graphicsfeature) -nebula_flatc(SYSTEM graphicsfeature/graphicsfeatureschema.fbs graphicsfeature/terrainschema.fbs graphicsfeature/vegetationschema.fbs) fips_deps(render application dynui tbui nflatbuffer) target_precompile_headers(graphicsfeature PRIVATE ) fips_ide_group(features) @@ -20,6 +19,7 @@ fips_dir(components) decal.json lighting.json model.json + gi.json ) nebula_end_module() diff --git a/code/addons/graphicsfeature/components/gi.json b/code/addons/graphicsfeature/components/gi.json new file mode 100644 index 000000000..9d756e46b --- /dev/null +++ b/code/addons/graphicsfeature/components/gi.json @@ -0,0 +1,81 @@ +{ + "namespace": "GraphicsFeature", + "components": { + "DDGIVolume": { + "graphicsEntityId": { + "type": "uint", + "default": -1, + "hideInInspector": true + }, + "numProbesX": { + "type": "uint", + "default": 8 + }, + "numProbesY": { + "type": "uint", + "default": 8 + }, + "numProbesZ": { + "type": "uint", + "default": 8 + }, + "numRaysPerProbe": { + "type": "uint", + "default": 188 + }, + "relocate": { + "type": "bool", + "default": false, + "description": "Relocate probes if they are inside geometry" + }, + "scrolling": { + "type": "bool", + "default": false, + "description": "Scroll probes infinitely based on camera position" + }, + "updateBudget": { + "type": "float", + "default": 1.0, + "description": "Percentage of probes to update per frame, 1.0 means 100%" + }, + "classify": { + "type": "bool", + "default": false, + "description": "Automatically disable probes that hit no geometry. This is to save performance. Said probes shoot a minimal set of rays to check if they should invalidate their status" + }, + "irradianceScale": { + "type": "float", + "default": 1, + "description": "Energy of output irradiance" + }, + "normalBias": { + "type": "float", + "default": 0.1, + "description": "Adds distance on surface along normal direction to avoid aliasing" + }, + "viewBias": { + "type": "float", + "default": 0.1, + "description": "Adds distance on surface along camera view direction to avoid aliasing" + }, + "distanceExponent": { + "type": "float", + "default": 50 + }, + "hysteresis": { + "type": "float", + "default": 0.97 + }, + "borderBlendCutoff": { + "type": "float", + "default": 0.0, + "description": "Percentage distance from the volume edge where blending should start" + }, + "borderBlend": { + "type": "float", + "default": 0.0, + "description": "Blend gradient factor based on cutoff point and outer edge of volume" + } + } + } +} diff --git a/code/addons/graphicsfeature/components/model.json b/code/addons/graphicsfeature/components/model.json index 8a86f8a0d..56636261b 100644 --- a/code/addons/graphicsfeature/components/model.json +++ b/code/addons/graphicsfeature/components/model.json @@ -11,7 +11,10 @@ "default": -1, "hideInInspector": true }, - "raytracing": "bool", + "raytracing": { + "type": "bool", + "default": true + }, "anim": { "type": "resource", "default": "" diff --git a/code/addons/graphicsfeature/graphicsfeatureunit.cc b/code/addons/graphicsfeature/graphicsfeatureunit.cc index 3c6c8547c..78a54d41e 100644 --- a/code/addons/graphicsfeature/graphicsfeatureunit.cc +++ b/code/addons/graphicsfeature/graphicsfeatureunit.cc @@ -33,10 +33,8 @@ #include "graphicsfeature/managers/cameramanager.h" #include "nflatbuffer/nebula_flat.h" -#include "flat/graphicsfeature/graphicsfeatureschema.h" -#include "flat/graphicsfeature/terrainschema.h" -#include "flat/graphicsfeature/vegetationschema.h" #include "nflatbuffer/flatbufferinterface.h" +#include "flat/options/levelsettings.h" #include "components/camera.h" #include "components/decal.h" #include "components/lighting.h" @@ -48,6 +46,7 @@ #include "frame/default.h" #include "frame/shadows.h" +#include "gi/ddgicontext.h" #if WITH_NEBULA_EDITOR #include "frame/editorframe.h" #endif @@ -89,6 +88,7 @@ GraphicsFeatureUnit::OnAttach() this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitPointLight }); this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitSpotLight }); this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitAreaLight }); + this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitDDGIVolume}); this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitModel }); this->RegisterComponentType({.decay = true, .OnInit = &GraphicsManager::InitDecal}); this->RegisterComponentType(); @@ -112,20 +112,15 @@ GraphicsFeatureUnit::OnActivate() this->inputServer->Open(); Util::String contents; - const IO::URI terrainSetupPath("data:tables/graphicsfeature/terrain.pter"); - GraphicsFeature::TerrainSetupT terrainSettings; - if (IO::IoServer::Instance()->ReadFile(terrainSetupPath, contents)) + const IO::URI levelSetupPath("tbl:app/base_level.nlst"); + App::LevelSettingsT levelSettings; + if (IO::IoServer::Instance()->ReadFile(levelSetupPath, contents)) { - Flat::FlatbufferInterface::DeserializeFlatbuffer(terrainSettings, (byte*)contents.AsCharPtr()); + Flat::FlatbufferInterface::DeserializeFlatbuffer(levelSettings, (byte*)contents.AsCharPtr()); } + auto& terrainSettings = levelSettings.terrain_setup; + auto& vegetationSettings = levelSettings.vegetation_setup; - const IO::URI vegetationSetupPath("data:tables/graphicsfeature/vegetation.pveg"); - GraphicsFeature::VegetationSetupT vegetationSettings; - if (IO::IoServer::Instance()->ReadFile(vegetationSetupPath, contents)) - { - Flat::FlatbufferInterface::DeserializeFlatbuffer(vegetationSettings, (byte*)contents.AsCharPtr()); - } - SizeT width = this->GetCmdLineArgs().GetInt("-w", 1280); SizeT height = this->GetCmdLineArgs().GetInt("-h", 1024); @@ -175,28 +170,28 @@ GraphicsFeatureUnit::OnActivate() Raytracing::RaytracingContext::Create(raytracingSettings); Clustering::ClusterContext::Create(0.01f, 1000.0f, this->wnd); - if (terrainSettings.config && terrainSettings.config->use) + if (terrainSettings && terrainSettings->config && terrainSettings->config->use) { Terrain::TerrainSetupSettings settings { - terrainSettings.config->min_height, - terrainSettings.config->max_height, // Min/max height - terrainSettings.config->world_size_width, - terrainSettings.config->world_size_height, // World size in meters - terrainSettings.config->tile_size_width, - terrainSettings.config->tile_size_height, // Tile size in meters - terrainSettings.config->quads_per_tile_width, - terrainSettings.config->quads_per_tile_height, // Amount of quads per tile + terrainSettings->config->min_height, + terrainSettings->config->max_height, // Min/max height + terrainSettings->config->world_size_width, + terrainSettings->config->world_size_height, // World size in meters + terrainSettings->config->tile_size_width, + terrainSettings->config->tile_size_height, // Tile size in meters + terrainSettings->config->quads_per_tile_width, + terrainSettings->config->quads_per_tile_height, // Amount of quads per tile }; Terrain::TerrainContext::Create(settings); Terrain::TerrainContext::SetSun(this->globalLight); this->terrain.entity = Graphics::CreateEntity(); Graphics::RegisterEntity(this->terrain.entity); - Terrain::TerrainContext::SetupTerrain(this->terrain.entity, terrainSettings.instance->height, terrainSettings.instance->decision, terrainSettings.config->raytracing); + Terrain::TerrainContext::SetupTerrain(this->terrain.entity, terrainSettings->instance->height, terrainSettings->instance->decision, terrainSettings->config->raytracing); - for (IndexT i = 0; i < terrainSettings.biomes.size(); i++) + for (IndexT i = 0; i < terrainSettings->biomes.size(); i++) { - const std::unique_ptr& settings = terrainSettings.biomes[i]; + const std::unique_ptr& settings = terrainSettings->biomes[i]; Terrain::BiomeParameters biomeParams = { @@ -237,20 +232,21 @@ GraphicsFeatureUnit::OnActivate() this->terrain.biomes.Append(biome); } - if (vegetationSettings.use) + if (vegetationSettings && vegetationSettings->use) { Vegetation::VegetationSetupSettings vegSettings { - terrainSettings.config->min_height, - terrainSettings.config->max_height, // min/max height - Math::vec2 {terrainSettings.config->world_size_width, terrainSettings.config->world_size_height}}; + terrainSettings->config->min_height, + terrainSettings->config->max_height, // min/max height + Math::vec2 {terrainSettings->config->world_size_width, terrainSettings->config->world_size_height}}; Vegetation::VegetationContext::Create(vegSettings); } } - + Lighting::LightContext::Create(); Decals::DecalContext::Create(); Characters::CharacterContext::Create(); Fog::VolumetricFogContext::Create(); + GI::DDGIContext::Create(); PostEffects::BloomContext::Create(); PostEffects::SSAOContext::Create(); PostEffects::HistogramContext::Create(); @@ -282,7 +278,7 @@ GraphicsFeatureUnit::OnActivate() }); Lighting::LightContext::RegisterEntity(this->globalLight); - Lighting::LightContext::SetupGlobalLight(this->globalLight, Math::vec3(1), 50.000f, Math::vec3(0, 0, 0), Math::vec3(0, 0, 0), 0, 70_rad, 0_rad, true); + Lighting::LightContext::SetupGlobalLight(this->globalLight, Math::vec3(1), 50.000f, Math::vec3(0, 0, 0), 70_rad, 0_rad, true); ObserverContext::CreateBruteforceSystem({}); @@ -314,6 +310,7 @@ GraphicsFeatureUnit::OnActivate() Fog::VolumetricFogContext::UpdateViewDependentResources, Lighting::LightContext::UpdateViewDependentResources, Raytracing::RaytracingContext::UpdateViewResources, + GI::DDGIContext::UpdateActiveVolumes, }; Util::Array postLogicCalls = @@ -334,14 +331,14 @@ GraphicsFeatureUnit::OnActivate() Util::Array postLogicViewCalls = { }; - - if (terrainSettings.config && terrainSettings.config->use) + + if (terrainSettings->config && terrainSettings->config->use) { preLogicCalls.Append(Terrain::TerrainContext::RenderUI); preLogicViewCalls.Append(Terrain::TerrainContext::CullPatches); postLogicViewCalls.Append(Terrain::TerrainContext::UpdateLOD); - if (vegetationSettings.use) + if (vegetationSettings->use) { postLogicViewCalls.Append(Vegetation::VegetationContext::UpdateViewResources); } diff --git a/code/addons/graphicsfeature/managers/graphicsmanager.cc b/code/addons/graphicsfeature/managers/graphicsmanager.cc index 0f11e2f2d..6c9eddc26 100644 --- a/code/addons/graphicsfeature/managers/graphicsmanager.cc +++ b/code/addons/graphicsfeature/managers/graphicsmanager.cc @@ -20,6 +20,7 @@ #include "lighting/lightcontext.h" #include "game/componentinspection.h" #include "decals/decalcontext.h" +#include "gi/ddgicontext.h" namespace GraphicsFeature { @@ -147,6 +148,22 @@ DeregisterDecal(Graphics::GraphicsEntityId gfxId) Graphics::DestroyEntity(gfxId); } +//------------------------------------------------------------------------------ +/** +*/ +void +DeregisterDDGIVolume(Graphics::GraphicsEntityId gfxId) +{ + if (gfxId == Graphics::InvalidGraphicsEntityId) + return; + + if (GI::DDGIContext::IsEntityRegistered(gfxId)) + { + GI::DDGIContext::DeregisterEntity(gfxId); + } + Graphics::DestroyEntity(gfxId); +} + //------------------------------------------------------------------------------ /** */ @@ -194,6 +211,14 @@ GraphicsManager::OnDecay() Decal* decal = decalData + i; DeregisterDecal(decal->graphicsEntityId); } + + Game::ComponentDecayBuffer const ddgiVolumeDecayBuffer = world->GetDecayBuffer(Game::GetComponentId()); + DDGIVolume* ddgiVolumeData = (DDGIVolume*)ddgiVolumeDecayBuffer.buffer; + for (int i = 0; i < ddgiVolumeDecayBuffer.size; i++) + { + DDGIVolume* volume = ddgiVolumeData + i; + DeregisterDDGIVolume(volume->graphicsEntityId); + } } //------------------------------------------------------------------------------ @@ -297,6 +322,32 @@ GraphicsManager::InitUpdateDecalTransformProcessor() .Build(); } +//------------------------------------------------------------------------------ +/** +*/ +void +GraphicsManager::InitUpdateDDGIVolumeTransformProcessor() +{ + Game::World* world = Game::GetWorld(WORLD_DEFAULT); + + Game::ProcessorBuilder(world, "GraphicsManager.UpdateDDGIVolumeTransform"_atm) + .On("OnEndFrame") + .Excluding() + .Func( + [](Game::World* world, + Game::Position const& pos, + Game::Orientation const& rot, + Game::Scale const& scale, + GraphicsFeature::DDGIVolume const& volume) + { + Math::mat4 transform = Math::trs(pos, rot, scale); + GI::DDGIContext::SetPosition(volume.graphicsEntityId, pos); + GI::DDGIContext::SetSize(volume.graphicsEntityId, scale); + } + ) + .Build(); +} + //------------------------------------------------------------------------------ /** */ @@ -307,6 +358,7 @@ GraphicsManager::OnActivate() this->InitUpdateModelTransformProcessor(); this->InitUpdateLightTransformProcessor(); this->InitUpdateDecalTransformProcessor(); + this->InitUpdateDDGIVolumeTransformProcessor(); } //------------------------------------------------------------------------------ @@ -406,12 +458,45 @@ GraphicsManager::InitDecal(Game::World* world, Game::Entity entity, Decal* decal // decal->graphicsEntityId, // transform, // decal->resource, // TODO: load resource - // + // //); //Math::mat4 transform = Math::trs(pos, rot, scale); //Decals::DecalContext::SetTransform(decal->graphicsEntityId, transform); } +//------------------------------------------------------------------------------ +/** +*/ +void +GraphicsManager::InitDDGIVolume(Game::World* world, Game::Entity entity, DDGIVolume* volume) +{ + if (!CoreGraphics::RayTracingSupported) + return; + volume->graphicsEntityId = Graphics::CreateEntity().id; + GI::DDGIContext::RegisterEntity(volume->graphicsEntityId); + GI::DDGIContext::VolumeSetup setup; + setup.numProbesX = volume->numProbesX; + setup.numProbesY = volume->numProbesY; + setup.numProbesZ = volume->numProbesZ; + setup.numRaysPerProbe = volume->numRaysPerProbe; + setup.position = world->GetComponent(entity); + setup.size = world->GetComponent(entity); + setup.irradianceScale = volume->irradianceScale; + setup.normalBias = volume->normalBias; + setup.viewBias = volume->viewBias; + setup.distanceExponent = volume->distanceExponent; + setup.hysteresis = volume->hysteresis; + setup.blend = volume->borderBlend; + setup.blendCutoff = volume->borderBlendCutoff; + setup.updateBudget = volume->updateBudget; + + setup.options.flags.relocate = volume->relocate; + setup.options.flags.scrolling = volume->scrolling; + setup.options.flags.classify = volume->classify; + + GI::DDGIContext::SetupVolume(volume->graphicsEntityId, setup); +} + //------------------------------------------------------------------------------ /** */ @@ -519,6 +604,23 @@ GraphicsManager::OnCleanup(Game::World* world) } } + Game::DestroyFilter(filter); + } + { // DDGIVolume cleanup + Game::Filter filter = Game::FilterBuilder().Including().Build(); + Game::Dataset data = world->Query(filter); + + for (int v = 0; v < data.numViews; v++) + { + Game::Dataset::View const& view = data.views[v]; + DDGIVolume const* const componentData = (DDGIVolume*)view.buffers[0]; + + for (IndexT i = 0; i < view.numInstances; ++i) + { + DeregisterDDGIVolume((componentData + i)->graphicsEntityId); + } + } + Game::DestroyFilter(filter); } } diff --git a/code/addons/graphicsfeature/managers/graphicsmanager.h b/code/addons/graphicsfeature/managers/graphicsmanager.h index 9f7809f6c..eb520a52f 100644 --- a/code/addons/graphicsfeature/managers/graphicsmanager.h +++ b/code/addons/graphicsfeature/managers/graphicsmanager.h @@ -20,6 +20,7 @@ #include "components/decal.h" #include "components/lighting.h" #include "components/model.h" +#include "components/gi.h" namespace GraphicsFeature { @@ -47,11 +48,14 @@ class GraphicsManager : public Game::Manager static void InitAreaLight(Game::World* world, Game::Entity entity, AreaLight* light); /// called automatically when a decal needs to be initialized static void InitDecal(Game::World* world, Game::Entity entity, Decal* decal); + /// called automatically when a ddgi volume needs to be initialized + static void InitDDGIVolume(Game::World* world, Game::Entity entity, DDGIVolume* volume); private: void InitUpdateModelTransformProcessor(); void InitUpdateLightTransformProcessor(); void InitUpdateDecalTransformProcessor(); + void InitUpdateDDGIVolumeTransformProcessor(); }; } // namespace GraphicsFeature diff --git a/code/addons/nflatbuffer/flatbufferinterface.cc b/code/addons/nflatbuffer/flatbufferinterface.cc index 34d41b565..dc383e4c8 100644 --- a/code/addons/nflatbuffer/flatbufferinterface.cc +++ b/code/addons/nflatbuffer/flatbufferinterface.cc @@ -173,6 +173,31 @@ FlatbufferInterface::ParseJson(IO::URI const& file) return Util::Blob(); } +//------------------------------------------------------------------------------ +/** +*/ +Util::Blob +FlatbufferInterface::ParseJson(IO::URI const& file, const Util::String& schema) +{ + Util::String contents; + if (IO::IoServer::Instance()->ReadFile(file, contents)) + { + IndexT schemaIndex = state.schemaFiles.FindIndex(schema.AsCharPtr()); + n_assert(schemaIndex != InvalidIndex); + flatbuffers::Parser* parser = CreateParser(state.schemaFiles.ValueAtIndex(schemaIndex)); + if (parser->ParseJson(contents.AsCharPtr(),file.LocalPath().AsCharPtr())) + { + auto builderSize = parser->builder_.GetSize(); + char* builderData = reinterpret_cast(parser->builder_.GetBufferPointer()); + Util::Blob blob(builderData, builderSize); + delete parser; + return blob; + } + delete parser; + } + return Util::Blob(); +} + //------------------------------------------------------------------------------ /** */ diff --git a/code/addons/nflatbuffer/flatbufferinterface.h b/code/addons/nflatbuffer/flatbufferinterface.h index ca2987da6..dfca11fcd 100644 --- a/code/addons/nflatbuffer/flatbufferinterface.h +++ b/code/addons/nflatbuffer/flatbufferinterface.h @@ -35,6 +35,8 @@ class FlatbufferInterface /// static Util::Blob ParseJson(IO::URI const& file); /// + static Util::Blob ParseJson(IO::URI const& file, const Util::String& schema); + /// static bool HasSchema(Util::StringAtom identifier); /// Helper function, use SerializeFlatbuffer macro instead @@ -119,7 +121,7 @@ template bool FlatbufferInterface::DeserializeJsonFlatbuffer(ItemT& item, const IO::URI& file, const Util::String& rootName) { - Util::Blob flatBuffer = FlatbufferInterface::ParseJson(file); + Util::Blob flatBuffer = FlatbufferInterface::ParseJson(file, rootName); if (flatBuffer.IsValid()) { const BaseT* bItem = flatbuffers::GetRoot(flatBuffer.GetPtr()); diff --git a/code/application/CMakeLists.txt b/code/application/CMakeLists.txt index 549d9a39e..48355431e 100644 --- a/code/application/CMakeLists.txt +++ b/code/application/CMakeLists.txt @@ -5,7 +5,7 @@ nebula_begin_module(application) nebula_add_blueprints() target_include_directories(application PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - fips_deps(foundation audio resource scripting memdb imgui input) + fips_deps(foundation audio resource scripting memdb imgui input options) fips_dir(.) if (FIPS_WINDOWS) fips_files(application.natvis) diff --git a/code/application/appgame/gameapplication.cc b/code/application/appgame/gameapplication.cc index a8510b63f..8e9e340d5 100644 --- a/code/application/appgame/gameapplication.cc +++ b/code/application/appgame/gameapplication.cc @@ -5,6 +5,8 @@ //------------------------------------------------------------------------------ #include "appgame/gameapplication.h" + +#include "options.h" #include "core/debug/corepagehandler.h" #include "threading/debug/threadpagehandler.h" #include "memory/debug/memorypagehandler.h" @@ -27,7 +29,6 @@ __ImplementSingleton(App::GameApplication); IndexT GameApplication::FrameIndex = -1; bool GameApplication::editorEnabled = false; - using namespace Util; using namespace Core; using namespace IO; @@ -43,7 +44,6 @@ GameApplication::GameApplication() : ,defaultTcpPort(2100) #endif { - __ConstructSingleton; } @@ -72,7 +72,7 @@ GameApplication::Open() this->coreServer = CoreServer::Create(); this->coreServer->SetCompanyName(Application::Instance()->GetCompanyName()); this->coreServer->SetAppName(Application::Instance()->GetAppTitle()); - + Util::String root = IO::FSWrapper::GetHomeDirectory(); #if PUBLIC_BUILD @@ -80,7 +80,7 @@ GameApplication::Open() { root = System::NebulaSettings::ReadString(Application::Instance()->GetCompanyName(),Application::Instance()->GetAppTitle(),"path"); } -#else +#else if(System::NebulaSettings::Exists("gscept", "ToolkitShared", "workdir")) { root = System::NebulaSettings::ReadString("gscept", "ToolkitShared", "workdir"); @@ -91,7 +91,7 @@ GameApplication::Open() } #endif this->coreServer->SetRootDirectory(root); - this->coreServer->Open(); + this->coreServer->Open(); // setup game content server this->gameContentServer = GameContentServer::Create(); @@ -108,6 +108,8 @@ GameApplication::Open() Flat::FlatbufferInterface::Init(); + Options::InitOptions(); + Jobs2::JobSystemInitInfo jobSystemInfo; jobSystemInfo.numThreads = System::NumCpuCores; jobSystemInfo.name = "JobSystem"; @@ -163,7 +165,7 @@ GameApplication::Open() this->gameServer->Start(); // setup profiling stuff _setup_grouped_timer(GameApplicationFrameTimeAll, "Game Subsystem"); - + return true; } @@ -197,7 +199,7 @@ GameApplication::Close() this->gameContentServer = nullptr; this->resourceServer->Close(); - this->resourceServer = nullptr; + this->resourceServer = nullptr; #if __NEBULA_HTTP__ this->debugInterface->Close(); diff --git a/code/application/appgame/gameapplication.h b/code/application/appgame/gameapplication.h index 9604d1c14..ec0e2bf39 100644 --- a/code/application/appgame/gameapplication.h +++ b/code/application/appgame/gameapplication.h @@ -49,6 +49,7 @@ class GameApplication : public Application /// static bool IsEditorEnabled(); + protected: /// setup game features virtual void SetupGameFeatures(); @@ -64,8 +65,8 @@ class GameApplication : public Application Ptr ioInterface; Ptr baseGameFeature; - static bool editorEnabled; + static bool editorEnabled; #if __NEBULA_HTTP__ Ptr debugInterface; diff --git a/code/application/game/componentinspection.cc b/code/application/game/componentinspection.cc index 3b1613fc1..6236cd7e4 100644 --- a/code/application/game/componentinspection.cc +++ b/code/application/game/componentinspection.cc @@ -214,7 +214,7 @@ void ComponentDrawFuncT(Game::Entity owner, ComponentId component, void* data, bool* commit) { ImGui::PushID(component.id + 0x125233 + reinterpret_cast(data)); - if (ImGui::DragInt("##input_data", (int*)data), 1.0f, 0, 0xFFFFFFFF) + if (ImGui::DragInt("##input_data", (int*)data, 1.0f, 0, 0xFFFFFFFF)) *commit = true; ImGui::PopID(); } diff --git a/code/foundation/ids/idallocator.h b/code/foundation/ids/idallocator.h index 1fb377037..391c7aa23 100644 --- a/code/foundation/ids/idallocator.h +++ b/code/foundation/ids/idallocator.h @@ -127,10 +127,11 @@ class IdAllocatorSafe : public Util::ArrayAllocatorSafe alloc_for_each_in_tuple(this->objects); index = this->size++; this->owners.Append(Threading::Thread::GetMyThreadId()); + this->generations.Append(0); n_assert2(MAX_ALLOCS > index, "max amount of allocations exceeded!\n"); } this->allocationLock.Unlock(); - + Ids::Id8 generation = this->generations[index]; return index; } @@ -140,11 +141,13 @@ class IdAllocatorSafe : public Util::ArrayAllocatorSafe // TODO: We could possibly get better performance when defragging if we insert it in reverse order (high to low) this->allocationLock.Lock(); this->freeIds.Append(index); + this->generations[index]++; this->allocationLock.Unlock(); } private: Util::Array freeIds; + Util::Array generations; }; } // namespace Ids diff --git a/code/foundation/math/scalar.h b/code/foundation/math/scalar.h index 30d676515..b9e8633f2 100644 --- a/code/foundation/math/scalar.h +++ b/code/foundation/math/scalar.h @@ -258,6 +258,15 @@ fmod(scalar x, scalar y) return fmodf(x, y); } +//------------------------------------------------------------------------------ +/** +*/ +__forceinline scalar +fract(scalar x) +{ + return x - (int)x; +} + //------------------------------------------------------------------------------ /** Normalize an angular value into the range rad(0) to rad(360). diff --git a/code/foundation/memory/win32/win32memory.h b/code/foundation/memory/win32/win32memory.h index 0ea316e76..c348e182e 100644 --- a/code/foundation/memory/win32/win32memory.h +++ b/code/foundation/memory/win32/win32memory.h @@ -48,9 +48,6 @@ extern void FreeVirtual(void* ptr, size_t size); extern char* DuplicateCString(const char* from); /// check if 2 memory regions are overlapping extern bool IsOverlapping(const unsigned char* srcPtr, size_t srcSize, const unsigned char* dstPtr, size_t dstSize); -/// Allocate memory on the stack -#define StackAlloc(size) _malloca(size); -#define StackFree(ptr) _freea(ptr); //------------------------------------------------------------------------------ /** diff --git a/code/options/CMakeLists.txt b/code/options/CMakeLists.txt new file mode 100644 index 000000000..4862c877b --- /dev/null +++ b/code/options/CMakeLists.txt @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------------- +# Options +#------------------------------------------------------------------------------- + +nebula_begin_module(options) + target_include_directories(options PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CODE_ROOT}/foundation ${CODE_ROOT}/options) + target_precompile_headers(options PRIVATE ) + fips_deps(foundation) + fips_files( + options.cc + options.h + ) + nebula_flatc(SYSTEM options/projectsettings.fbs options/levelsettings.fbs) + +nebula_end_module() diff --git a/code/options/options.cc b/code/options/options.cc new file mode 100644 index 000000000..6fc1119f5 --- /dev/null +++ b/code/options/options.cc @@ -0,0 +1,18 @@ +//------------------------------------------------------------------------------ +// options.cc +// (C) 2025 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ +#include "options.h" +namespace Options +{ + +App::ProjectSettingsT ProjectSettings; +App::LevelSettingsT LevelSettings; +//------------------------------------------------------------------------------ +/** +*/ +void InitOptions() +{ + Flat::FlatbufferInterface::DeserializeJsonFlatbuffer(ProjectSettings, "proj:work/data/tables/projectsettings.json"_uri, "NPST"); +} +} diff --git a/code/options/options.h b/code/options/options.h new file mode 100644 index 000000000..20eec365d --- /dev/null +++ b/code/options/options.h @@ -0,0 +1,25 @@ +#pragma once +//------------------------------------------------------------------------------ +/** + Globally accessible options + + (C) 2025 Individual contributors, see AUTHORS file +*/ +//------------------------------------------------------------------------------ +#include "nflatbuffer/nebula_flat.h" +#include "nflatbuffer/flatbufferinterface.h" +#include "flat/options/levelsettings.h" +#include "flat/options/projectsettings.h" + +namespace Options +{ + +extern App::ProjectSettingsT ProjectSettings; +extern App::LevelSettingsT LevelSettings; + +//------------------------------------------------------------------------------ +/** +*/ +extern void InitOptions(); + +} // namespace Options diff --git a/code/physics/physics/streamactorpool.cc b/code/physics/physics/streamactorpool.cc index ecd3eb81a..b2852c29f 100644 --- a/code/physics/physics/streamactorpool.cc +++ b/code/physics/physics/streamactorpool.cc @@ -207,15 +207,13 @@ CreateMeshFromResource(MeshTopology type, Util::StringAtom resource, int primGro n_assert(header->numMeshes > 0); CoreGraphics::Nvx3Elements elements; - CoreGraphics::Nvx3::FillNvx3Elements(header, elements); - - const CoreGraphics::Nvx3Group& group = elements.groups[primGroup]; + CoreGraphics::Nvx3::FillNvx3Elements((char*)mapPtr, header, elements); const uint vertexStride = sizeof(CoreGraphics::BaseVertex); const ubyte* groupVertexBase = elements.vertexData + elements.ranges[0].baseVertexByteOffset; - const uint vertexCount = (elements.ranges[0].attributesVertexByteOffset - elements.ranges[0].baseVertexByteOffset) / vertexStride; - + const CoreGraphics::Nvx3Group* group = (CoreGraphics::Nvx3Group*)((char*)mapPtr + elements.ranges[0].firstGroupOffset + sizeof(CoreGraphics::Nvx3Group) * primGroup); + switch (type) { // FIXME, cooking doesnt seem to like our meshes, @@ -252,7 +250,7 @@ CreateMeshFromResource(MeshTopology type, Util::StringAtom resource, int primGro meshDesc.points.stride = vertexStride; meshDesc.points.data = groupVertexBase; - meshDesc.triangles.count = group.numIndices / 3; + meshDesc.triangles.count = group->numIndices / 3; if (elements.ranges[0].indexType == CoreGraphics::IndexType::Index16) { meshDesc.flags |= PxMeshFlag::e16_BIT_INDICES; @@ -333,6 +331,7 @@ AddMeshColliders(PhysicsResource::MeshColliderT* colliderNode, Math::transform c n_assert(nullptr == mapPtr); // map the stream to memory mapPtr = stream->MemoryMap(); + char* basePtr = (char*)mapPtr; n_assert(nullptr != mapPtr); auto header = (CoreGraphics::Nvx3Header*)mapPtr; @@ -348,9 +347,9 @@ AddMeshColliders(PhysicsResource::MeshColliderT* colliderNode, Math::transform c n_assert(header->numMeshes > 0); CoreGraphics::Nvx3Elements elements; - CoreGraphics::Nvx3::FillNvx3Elements(header, elements); + CoreGraphics::Nvx3::FillNvx3Elements(basePtr, header, elements); - const CoreGraphics::Nvx3Group& group = elements.groups[primGroup]; + const CoreGraphics::Nvx3Group* group = (CoreGraphics::Nvx3Group*)(basePtr + elements.ranges[0].firstGroupOffset + sizeof(CoreGraphics::Nvx3Group) * primGroup); const uint vertexStride = sizeof(CoreGraphics::BaseVertex); const ubyte* groupVertexBase = elements.vertexData + elements.ranges[0].baseVertexByteOffset; diff --git a/code/render/CMakeLists.txt b/code/render/CMakeLists.txt index 6a5b4cbb1..64e10030b 100644 --- a/code/render/CMakeLists.txt +++ b/code/render/CMakeLists.txt @@ -12,7 +12,7 @@ nebula_begin_module(render) add_nebula_shaders() set(target_has_shaders 1) target_include_directories(render PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CODE_ROOT}/addons ${CODE_ROOT}/render) - fips_deps(foundation input resource anyfx imgui) + fips_deps(foundation input resource anyfx imgui options) if (TARGET system_resources-res) target_link_libraries(render $) endif() @@ -418,7 +418,7 @@ ENDIF() ddgicontext.h ) fips_dir(gi/shaders) - add_shaders(probeupdate.fx) + add_shaders(probe_shared.fxh probe_update.fx probe_finalize.fx probe_debug.fx gi_volume_cull.fx probe_relocate_and_classify.fx) fips_dir(graphics) fips_files( bindlessregistry.cc diff --git a/code/render/characters/charactercontext.cc b/code/render/characters/charactercontext.cc index 4ad0f8b09..26370757b 100644 --- a/code/render/characters/charactercontext.cc +++ b/code/render/characters/charactercontext.cc @@ -758,7 +758,7 @@ CharacterContext::UpdateAnimations(const Graphics::FrameContext& ctx) } // Update skinning palette - uint offset = CoreGraphics::SetConstants(usedMatrices.Begin(), usedMatrices.Size()); + uint64 offset = CoreGraphics::SetConstants(usedMatrices.Begin(), usedMatrices.Size()); renderables.nodeStates[node].resourceTableOffsets[renderables.nodeStates[node].skinningConstantsIndex] = offset; } diff --git a/code/render/coregraphics/accelerationstructure.h b/code/render/coregraphics/accelerationstructure.h index f2b1a2956..fd0549569 100644 --- a/code/render/coregraphics/accelerationstructure.h +++ b/code/render/coregraphics/accelerationstructure.h @@ -39,9 +39,9 @@ struct BlasCreateInfo CoreGraphics::BufferId vbo, ibo; CoreGraphics::IndexType::Code indexType; CoreGraphics::VertexComponent::Format positionsFormat; - IndexT stride; - IndexT vertexOffset, indexOffset; - Util::Array primitiveGroups; + uint64 stride; + uint64 vertexOffset, indexOffset; + CoreGraphics::PrimitiveGroup primGroup; AccelerationStructureBuildFlags flags; }; diff --git a/code/render/coregraphics/barrier.h b/code/render/coregraphics/barrier.h index f80acf14b..dfe15a415 100644 --- a/code/render/coregraphics/barrier.h +++ b/code/render/coregraphics/barrier.h @@ -95,14 +95,14 @@ struct TextureSubresourceInfo struct BufferSubresourceInfo { - uint offset, size; + uint64 offset, size; BufferSubresourceInfo() : offset(0), size(NEBULA_WHOLE_BUFFER_SIZE) {} - BufferSubresourceInfo(uint offset, uint size) : + BufferSubresourceInfo(uint64 offset, uint64 size) : offset(offset), size(size) {} diff --git a/code/render/coregraphics/buffer.cc b/code/render/coregraphics/buffer.cc index e35c41fbb..1f00fe36e 100644 --- a/code/render/coregraphics/buffer.cc +++ b/code/render/coregraphics/buffer.cc @@ -95,6 +95,8 @@ BufferWithStaging::BufferWithStaging(BufferWithStaging&& rhs) if (this->deviceBuffer != InvalidBufferId) DestroyBuffer(this->deviceBuffer); this->deviceBuffer = std::move(rhs.deviceBuffer); + rhs.deviceBuffer = CoreGraphics::InvalidBufferId; + rhs.hostBuffers.buffers.Clear(); } //------------------------------------------------------------------------------ @@ -107,6 +109,8 @@ BufferWithStaging::operator=(BufferWithStaging&& rhs) if (this->deviceBuffer != InvalidBufferId) DestroyBuffer(this->deviceBuffer); this->deviceBuffer = std::move(rhs.deviceBuffer); + rhs.deviceBuffer = CoreGraphics::InvalidBufferId; + rhs.hostBuffers.buffers.Clear(); } //------------------------------------------------------------------------------ @@ -144,6 +148,8 @@ BufferWithStaging::Flush(const CoreGraphics::CmdBufferId cmdBuf, SizeT numBytes) */ BufferWithStaging::~BufferWithStaging() { + if (this->deviceBuffer != InvalidBufferId) + DestroyBuffer(this->deviceBuffer); } } // namespace CoreGraphics diff --git a/code/render/coregraphics/buffer.h b/code/render/coregraphics/buffer.h index ba6f6ee2b..dc0484598 100644 --- a/code/render/coregraphics/buffer.h +++ b/code/render/coregraphics/buffer.h @@ -103,13 +103,13 @@ void DestroyBuffer(const BufferId id); /// get type of buffer const BufferUsageFlags BufferGetType(const BufferId id); /// get buffer size, which is the number of elements -const SizeT BufferGetSize(const BufferId id); +const uint64 BufferGetSize(const BufferId id); /// get buffer element size, this is the size of a single element, like a vertex or index, and is multiplied with the size -const SizeT BufferGetElementSize(const BufferId id); +const uint64 BufferGetElementSize(const BufferId id); /// get buffer total byte size -const SizeT BufferGetByteSize(const BufferId id); +const uint64 BufferGetByteSize(const BufferId id); /// get maximum size of upload for BufferUpload -const SizeT BufferGetUploadMaxSize(); +const uint64 BufferGetUploadMaxSize(); /// map memory void* BufferMap(const BufferId id); @@ -138,9 +138,9 @@ template void BufferUpload(const CoreGraphics::CmdBufferId cmdBuf, c void BufferFill(const CoreGraphics::CmdBufferId cmdBuf, const BufferId id, char pattern); /// flush any changes done to the buffer on the CPU side so they are visible on the GPU -void BufferFlush(const BufferId id, IndexT offset = 0, SizeT size = NEBULA_WHOLE_BUFFER_SIZE); +void BufferFlush(const BufferId id, uint64 offset = 0ull, uint64 size = NEBULA_WHOLE_BUFFER_SIZE); /// invalidate buffer CPU side, such that any GPU changes will be made visible -void BufferInvalidate(const BufferId id, IndexT offset = 0, SizeT size = NEBULA_WHOLE_BUFFER_SIZE); +void BufferInvalidate(const BufferId id, uint64 offset = 0ull, uint64 size = NEBULA_WHOLE_BUFFER_SIZE); /// Evict a page void BufferSparseEvict(const BufferId id, IndexT pageIndex); diff --git a/code/render/coregraphics/commandbuffer.h b/code/render/coregraphics/commandbuffer.h index 57de46c7f..7c31b32ea 100644 --- a/code/render/coregraphics/commandbuffer.h +++ b/code/render/coregraphics/commandbuffer.h @@ -194,7 +194,7 @@ void CmdSetPrimitiveTopology(const CmdBufferId id, const CoreGraphics::Primitive /// Set shader program void CmdSetShaderProgram(const CmdBufferId id, const CoreGraphics::ShaderProgramId pro, bool bindGlobals = true); /// Set resource table -void CmdSetResourceTable(const CmdBufferId id, const CoreGraphics::ResourceTableId table, const IndexT slot, CoreGraphics::ShaderPipeline pipeline, const Util::FixedArray& offsets); +void CmdSetResourceTable(const CmdBufferId id, const CoreGraphics::ResourceTableId table, const IndexT slot, CoreGraphics::ShaderPipeline pipeline, const Util::FixedArray& offsets = nullptr); /// Set resource table using raw offsets void CmdSetResourceTable(const CmdBufferId id, const CoreGraphics::ResourceTableId table, const IndexT slot, CoreGraphics::ShaderPipeline pipeline, uint32 numOffsets, uint32* offsets); /// Set push constants diff --git a/code/render/coregraphics/config.h b/code/render/coregraphics/config.h index 1da5b557a..2a4d73543 100644 --- a/code/render/coregraphics/config.h +++ b/code/render/coregraphics/config.h @@ -13,12 +13,12 @@ #include "util/string.h" #include "core/rttimacros.h" -#define NEBULA_WHOLE_BUFFER_SIZE (-1) +#define NEBULA_WHOLE_BUFFER_SIZE (~0ull) #define NEBULA_ALL_MIPS (-1) #define NEBULA_ALL_LAYERS (-1) namespace CoreGraphics { -typedef uint ConstantBufferOffset; +typedef uint64 ConstantBufferOffset; union InputAssemblyKey { diff --git a/code/render/coregraphics/graphicsdevice.h b/code/render/coregraphics/graphicsdevice.h index 4d18dd19a..374dbedb6 100644 --- a/code/render/coregraphics/graphicsdevice.h +++ b/code/render/coregraphics/graphicsdevice.h @@ -228,9 +228,9 @@ void SubmitImmediateCommandBuffers(); /// Unlock constants void UnlockConstantUpdates(); /// Allocate range of memory and set data, return offset (thread safe) -template uint SetConstants(const TYPE& data); +template uint64 SetConstants(const TYPE& data); /// Allocate range of memory and set data as an array of elements, return offset (thread safe) -template uint SetConstants(const TYPE* data, SizeT elements); +template uint64 SetConstants(const TYPE* data, SizeT elements); /// Set constants based on pre-allocated memory (thread safe) template void SetConstants(ConstantBufferOffset offset, const TYPE& data); /// Set constants based on pre-allocated memory (thread safe) diff --git a/code/render/coregraphics/mesh.cc b/code/render/coregraphics/mesh.cc index c5cad3c9e..86416c238 100644 --- a/code/render/coregraphics/mesh.cc +++ b/code/render/coregraphics/mesh.cc @@ -34,8 +34,6 @@ CreateMesh(const MeshCreateInfo& info) .vertexLayout = info.vertexLayout, .primitiveTopology = info.topology, .primitiveGroups = info.primitiveGroups, - .vertexAllocation = info.vertexBufferAllocation, - .indexAllocation = info.indexBufferAllocation }; meshAllocator.Set(id, internals); meshAllocator.Release(id); @@ -50,11 +48,6 @@ CreateMesh(const MeshCreateInfo& info) void DestroyMesh(const MeshId id) { - const CoreGraphics::VertexAlloc& vertices = meshAllocator.ConstGet(id.id).vertexAllocation; - const CoreGraphics::VertexAlloc& indices = meshAllocator.ConstGet(id.id).indexAllocation; - - CoreGraphics::DeallocateVertices(vertices); - CoreGraphics::DeallocateIndices(indices); meshAllocator.Dealloc(id.id); } @@ -97,7 +90,7 @@ MeshSetVertexBuffer(const MeshId id, const BufferId buffer, const IndexT stream) //------------------------------------------------------------------------------ /** */ -const uint +const uint64 MeshGetVertexOffset(const MeshId id, const IndexT stream) { return meshAllocator.ConstGet(id.id).streams[stream].offset; @@ -115,7 +108,7 @@ MeshGetIndexBuffer(const MeshId id) //------------------------------------------------------------------------------ /** */ -const uint +const uint64 MeshGetIndexOffset(const MeshId id) { return meshAllocator.ConstGet(id.id).indexBufferOffset; diff --git a/code/render/coregraphics/mesh.h b/code/render/coregraphics/mesh.h index b55dca9f5..c9dbd85cf 100644 --- a/code/render/coregraphics/mesh.h +++ b/code/render/coregraphics/mesh.h @@ -45,8 +45,6 @@ struct MeshCreateInfo VertexLayoutId vertexLayout; CoreGraphics::PrimitiveTopology::Code topology; Util::Array primitiveGroups; - - CoreGraphics::VertexAlloc vertexBufferAllocation, indexBufferAllocation; }; /// create new mesh @@ -63,11 +61,11 @@ const BufferId MeshGetVertexBuffer(const MeshId id, const IndexT stream); /// Set vertex buffer const void MeshSetVertexBuffer(const MeshId id, const BufferId buffer, const IndexT stream); /// Get mesh vertex offset -const uint MeshGetVertexOffset(const MeshId id, const IndexT stream); +const uint64 MeshGetVertexOffset(const MeshId id, const IndexT stream); /// get index buffer const BufferId MeshGetIndexBuffer(const MeshId id); /// Get index buffer base offset -const uint MeshGetIndexOffset(const MeshId id); +const uint64 MeshGetIndexOffset(const MeshId id); /// Get index type const IndexType::Code MeshGetIndexType(const MeshId id); /// Get topology @@ -93,7 +91,6 @@ struct __Mesh VertexLayoutId vertexLayout; CoreGraphics::PrimitiveTopology::Code primitiveTopology; Util::Array primitiveGroups; - CoreGraphics::VertexAlloc vertexAllocation, indexAllocation; }; typedef Ids::IdAllocatorSafe< diff --git a/code/render/coregraphics/meshloader.cc b/code/render/coregraphics/meshloader.cc index db18492a5..b4d02b5b1 100644 --- a/code/render/coregraphics/meshloader.cc +++ b/code/render/coregraphics/meshloader.cc @@ -32,18 +32,18 @@ MeshLoader::MeshLoader() CoreGraphics::VertexLayoutCreateInfo vlCreateInfo; vlCreateInfo.name = "Normal"_atm; vlCreateInfo.comps = { - VertexComponent{ VertexComponent::IndexName::Position, VertexComponent::Float3, 0 } + VertexComponent{ VertexComponent::IndexName::Position, VertexComponent::Float3, 0 } , VertexComponent{ VertexComponent::IndexName::TexCoord1, VertexComponent::Short2, 0 } - , VertexComponent{ VertexComponent::IndexName::Normal, VertexComponent::Byte4N, 1 } - , VertexComponent{ VertexComponent::IndexName::Tangent, VertexComponent::Byte4N, 1 } + , VertexComponent{ VertexComponent::IndexName::Normal, VertexComponent::Byte4N, 1 } + , VertexComponent{ VertexComponent::IndexName::Tangent, VertexComponent::Byte4N, 1 } }; Layouts[(uint)CoreGraphics::VertexLayoutType::Normal] = CreateVertexLayout(vlCreateInfo); vlCreateInfo.name = "SecondaryUVs"_atm; vlCreateInfo.comps = { - VertexComponent{ VertexComponent::IndexName::Position, VertexComponent::Float3, 0 } + VertexComponent{ VertexComponent::IndexName::Position, VertexComponent::Float3, 0 } , VertexComponent{ VertexComponent::IndexName::TexCoord1, VertexComponent::Short2, 0 } - , VertexComponent{ VertexComponent::IndexName::Normal, VertexComponent::Byte4N, 1 } + , VertexComponent{ VertexComponent::IndexName::Normal, VertexComponent::Byte4N, 1 } , VertexComponent{ VertexComponent::IndexName::Tangent, VertexComponent::Byte4N, 1 } , VertexComponent{ VertexComponent::IndexName::TexCoord2, VertexComponent::UShort2N, 1 } }; @@ -132,15 +132,15 @@ MeshLoader::StreamResource(const ResourceLoadJob& job) ResourceName name = job.name; MeshStreamData* streamData = (MeshStreamData*)stream.data; auto header = (Nvx3Header*)streamData->mappedData; + char* basePtr = (char*)streamData->mappedData; uint bitsToLoad = job.loadState.requestedBits & ~(job.loadState.loadedBits | job.loadState.pendingBits); n_assert(header->magic == NEBULA_NVX_MAGICNUMBER); n_assert(header->numMeshes > 0); - auto vertexRanges = (Nvx3VertexRange*)(header + 1); - auto groups = (Nvx3Group*)(vertexRanges + header->numMeshes); - auto vertexData = (ubyte*)(groups + header->numGroups); - auto indexData = (ubyte*)(vertexData + header->vertexDataSize); + auto vertexRanges = (Nvx3VertexRange*)(basePtr + header->meshDataOffset); + auto vertexData = (ubyte*)(basePtr + header->vertexDataOffset); + auto indexData = (ubyte*)(basePtr + header->indexDataOffset); //auto meshletData = (Nvx3Meshlet*)(indexData + header->indexDataSize); CoreGraphics::BufferId vbo = CoreGraphics::GetVertexBuffer(); @@ -330,7 +330,6 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo { n_assert(stream.isvalid()); - Util::Array primGroups; void* mapPtr = nullptr; Util::FixedArray meshes; @@ -340,12 +339,12 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo reader->SetStream(stream); if (reader->Open()) { - n_assert(0 == primGroups.Size()); n_assert(stream->CanBeMapped()); n_assert(nullptr == mapPtr); // map the stream to memory mapPtr = stream->MemoryMap(); + char* basePtr = (char*)mapPtr; n_assert(nullptr != mapPtr); @@ -357,10 +356,9 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo } n_assert(header->numMeshes > 0); - auto vertexRanges = (Nvx3VertexRange*)(header + 1); - auto groups = (Nvx3Group*)(vertexRanges + header->numMeshes); - auto vertexData = (ubyte*)(groups + header->numGroups); - auto indexData = (ubyte*)(vertexData + header->vertexDataSize); + auto vertexRanges = (Nvx3VertexRange*)(basePtr + header->meshDataOffset); + auto vertexData = (ubyte*)(basePtr + header->vertexDataOffset); + auto indexData = (ubyte*)(basePtr + header->indexDataOffset); //auto meshletData = (Nvx3Meshlet*)(indexData + header->indexDataSize); meshes.Resize(header->numMeshes); @@ -380,7 +378,7 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo // Allocate vertices from global repository vertexAllocation = CoreGraphics::AllocateVertices(header->vertexDataSize); streamData->vertexAllocationOffset = vertexAllocation; - + meshResourceAllocator.Set(meshResource.id, vertexAllocation); if (job.immediate) { BufferCopyWithStaging(CoreGraphics::GetVertexBuffer(), streamData->vertexAllocationOffset.offset, vertexData, header->vertexDataSize); @@ -392,34 +390,35 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo // Allocate vertices from global repository indexAllocation = CoreGraphics::AllocateIndices(header->indexDataSize); streamData->indexAllocationOffset = indexAllocation; - + meshResourceAllocator.Set(meshResource.id, vertexAllocation); if (job.immediate) { BufferCopyWithStaging(CoreGraphics::GetIndexBuffer(), streamData->indexAllocationOffset.offset, indexData, header->indexDataSize); } } - for (uint i = 0; i < header->numGroups; i++) - { - PrimitiveGroup group; - group.SetBaseIndex(groups[i].firstIndex); - group.SetNumIndices(groups[i].numIndices); - primGroups.Append(group); - } - for (uint i = 0; i < header->numMeshes; i++) { + Util::Array primGroups; + const Nvx3VertexRange& range = vertexRanges[i]; + + for (uint j = 0; j < range.numGroups; j++) + { + PrimitiveGroup group; + const Nvx3Group* nvxGroup = (Nvx3Group*)(basePtr + range.firstGroupOffset + j * sizeof(Nvx3Group)); + group.SetBaseIndex(nvxGroup->firstIndex); + group.SetNumIndices(nvxGroup->numIndices); + primGroups.Append(group); + } MeshCreateInfo mshInfo; - mshInfo.streams.Append({ vbo, (SizeT)(streamData->vertexAllocationOffset.offset + vertexRanges[i].baseVertexByteOffset), 0 }); - mshInfo.streams.Append({ vbo, (SizeT)(streamData->vertexAllocationOffset.offset + vertexRanges[i].attributesVertexByteOffset), 1 }); - mshInfo.indexBufferOffset = streamData->indexAllocationOffset.offset + (SizeT)vertexRanges[i].indexByteOffset; + mshInfo.streams.Append({ vbo, (SizeT)(streamData->vertexAllocationOffset.offset + range.baseVertexByteOffset), 0 }); + mshInfo.streams.Append({ vbo, (SizeT)(streamData->vertexAllocationOffset.offset + range.attributesVertexByteOffset), 1 }); + mshInfo.indexBufferOffset = streamData->indexAllocationOffset.offset + (SizeT)range.indexByteOffset; mshInfo.indexBuffer = ibo; mshInfo.topology = PrimitiveTopology::TriangleList; - mshInfo.indexType = vertexRanges[i].indexType; + mshInfo.indexType = range.indexType; mshInfo.primitiveGroups = primGroups; - mshInfo.vertexLayout = Layouts[(uint)vertexRanges[i].layout]; - mshInfo.vertexBufferAllocation = vertexAllocation; - mshInfo.indexBufferAllocation = indexAllocation; + mshInfo.vertexLayout = Layouts[(uint)range.layout]; mshInfo.name = job.name; MeshId mesh = CreateMesh(mshInfo); meshes[i] = mesh; @@ -429,7 +428,7 @@ MeshLoader::SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJo } // Update mesh allocator - meshResourceAllocator.Set<0>(meshResource.id, meshes); + meshResourceAllocator.Set(meshResource.id, meshes); return ret; } diff --git a/code/render/coregraphics/meshloader.h b/code/render/coregraphics/meshloader.h index 0f022563e..a62b4ee8e 100644 --- a/code/render/coregraphics/meshloader.h +++ b/code/render/coregraphics/meshloader.h @@ -28,6 +28,9 @@ class MeshLoader : public Resources::ResourceLoader bool copySource; }; + /// Get vertex layout + static const CoreGraphics::VertexLayoutId GetLayout(const CoreGraphics::VertexLayoutType type); + private: struct MeshStreamData @@ -47,9 +50,7 @@ class MeshLoader : public Resources::ResourceLoader /// Update intermediate loaded state void UpdateLoaderSyncState() override; - - /// Get vertex layout - static const CoreGraphics::VertexLayoutId GetLayout(const CoreGraphics::VertexLayoutType type); + /// setup mesh from nvx3 file in memory ResourceLoader::_StreamData SetupMeshFromNvx(const Ptr& stream, const ResourceLoadJob& job, const MeshResourceId meshResource); diff --git a/code/render/coregraphics/meshresource.cc b/code/render/coregraphics/meshresource.cc index 8fcd62187..8abc1ac20 100644 --- a/code/render/coregraphics/meshresource.cc +++ b/code/render/coregraphics/meshresource.cc @@ -31,7 +31,12 @@ MeshResourceGetNumMeshes(const MeshResourceId id) */ void DestroyMeshResource(const MeshResourceId id) { - auto meshes = meshResourceAllocator.Get<0>(id.id); + auto vertexData = meshResourceAllocator.Get(id.id); + auto indexData = meshResourceAllocator.Get(id.id); + auto meshes = meshResourceAllocator.Get(id.id); + + CoreGraphics::DeallocateVertices(vertexData); + CoreGraphics::DeallocateIndices(indexData); for (IndexT i = 0; i < meshes.Size(); i++) { DestroyMesh(meshes[i]); diff --git a/code/render/coregraphics/meshresource.h b/code/render/coregraphics/meshresource.h index b61d691f9..a276d33c0 100644 --- a/code/render/coregraphics/meshresource.h +++ b/code/render/coregraphics/meshresource.h @@ -25,8 +25,18 @@ const SizeT MeshResourceGetNumMeshes(const MeshResourceId id); /// Destroy void DestroyMeshResource(const MeshResourceId id); + +enum +{ + MeshResource_Meshes, + MeshResource_VertexData, + MeshResource_IndexData +}; + typedef Ids::IdAllocator< - Util::FixedArray + Util::FixedArray, + CoreGraphics::VertexAlloc, + CoreGraphics::VertexAlloc > MeshResourceAllocator; extern MeshResourceAllocator meshResourceAllocator; diff --git a/code/render/coregraphics/nvx3fileformatstructs.h b/code/render/coregraphics/nvx3fileformatstructs.h index 326378f5f..3304a6d1a 100644 --- a/code/render/coregraphics/nvx3fileformatstructs.h +++ b/code/render/coregraphics/nvx3fileformatstructs.h @@ -27,11 +27,14 @@ namespace CoreGraphics struct Nvx3Header { uint magic; + uint meshDataOffset; uint numMeshes; // The number of Nvx3Mesh structs - uint numGroups; + uint meshletDataOffset; uint numMeshlets; - uint indexDataSize; // The total byte size of the index data for all meshes + uint vertexDataOffset; uint vertexDataSize; + uint indexDataOffset; + uint indexDataSize; // The total byte size of the index data for all meshes }; struct Nvx3VertexRange @@ -39,6 +42,8 @@ struct Nvx3VertexRange uint indexByteOffset; uint baseVertexByteOffset; uint attributesVertexByteOffset; + uint firstGroupOffset; + uint numGroups; CoreGraphics::IndexType::Code indexType; CoreGraphics::VertexLayoutType layout; }; @@ -64,7 +69,6 @@ struct Nvx3Meshlet struct Nvx3Elements { CoreGraphics::Nvx3VertexRange* ranges; - CoreGraphics::Nvx3Group* groups; ubyte* vertexData; ubyte* indexData; CoreGraphics::Nvx3Meshlet* meshlets; @@ -72,7 +76,7 @@ struct Nvx3Elements namespace Nvx3 { - void FillNvx3Elements(Nvx3Header* header, Nvx3Elements& OutElements); + void FillNvx3Elements(char* basePointer, Nvx3Header* header, Nvx3Elements& OutElements); }; #pragma pack(pop) @@ -86,14 +90,13 @@ namespace Nvx3 /** */ inline void -FillNvx3Elements(Nvx3Header* header, Nvx3Elements& OutElements) +FillNvx3Elements(char* basePointer, Nvx3Header* header, Nvx3Elements& OutElements) { n_assert(header != nullptr); - OutElements.ranges = (CoreGraphics::Nvx3VertexRange*)(header + 1); - OutElements.groups = (CoreGraphics::Nvx3Group*)(OutElements.ranges + header->numMeshes); - OutElements.vertexData = (ubyte*)(OutElements.groups + header->numGroups); - OutElements.indexData = (ubyte*)(OutElements.vertexData + header->vertexDataSize); - OutElements.meshlets = (CoreGraphics::Nvx3Meshlet*)(OutElements.indexData + header->indexDataSize); + OutElements.ranges = (CoreGraphics::Nvx3VertexRange*)(basePointer + header->meshDataOffset); + OutElements.vertexData = (ubyte*)(basePointer + header->vertexDataOffset); + OutElements.indexData = (ubyte*)(basePointer + header->indexDataOffset); + OutElements.meshlets = (CoreGraphics::Nvx3Meshlet*)(basePointer + header->meshletDataOffset); } } // namespace Nvx3 diff --git a/code/render/coregraphics/pipeline.h b/code/render/coregraphics/pipeline.h index 15244adf7..1958a5b3b 100644 --- a/code/render/coregraphics/pipeline.h +++ b/code/render/coregraphics/pipeline.h @@ -36,7 +36,7 @@ struct PipelineRayTracingTable }; /// Create raytacing pipeline using multiple shader programs -const PipelineRayTracingTable CreateRaytracingPipeline(const Util::Array programs); +const PipelineRayTracingTable CreateRaytracingPipeline(const Util::Array programs, const CoreGraphics::QueueType queueType = CoreGraphics::QueueType::GraphicsQueueType); /// Destroy raytracing pipeline void DestroyRaytracingPipeline(const PipelineRayTracingTable& table); diff --git a/code/render/coregraphics/resourcetable.cc b/code/render/coregraphics/resourcetable.cc index 4ea9e7da0..3fd57aecb 100644 --- a/code/render/coregraphics/resourcetable.cc +++ b/code/render/coregraphics/resourcetable.cc @@ -14,9 +14,12 @@ ResourceTableSet::ResourceTableSet(const ResourceTableCreateInfo& createInfo) { const SizeT numTables = CoreGraphics::GetNumBufferedFrames(); this->tables.Resize(numTables); + const char* name = createInfo.name; for (IndexT i = 0; i < numTables; i++) { this->tables[i] = CreateResourceTable(createInfo); + if (createInfo.name != nullptr) + CoreGraphics::ObjectSetName(this->tables[i], Util::String::Sprintf("%s %d", createInfo.name, i).AsCharPtr()); } } diff --git a/code/render/coregraphics/resourcetable.h b/code/render/coregraphics/resourcetable.h index 76b5cdfd9..3ba2a53c7 100644 --- a/code/render/coregraphics/resourcetable.h +++ b/code/render/coregraphics/resourcetable.h @@ -271,7 +271,7 @@ struct ResourceTableBuffer , dynamicOffset(false) {}; - ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, SizeT size) + ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, uint64 size) : buf(buf) , slot(slot) , index(0) @@ -281,7 +281,7 @@ struct ResourceTableBuffer , dynamicOffset(false) {}; - ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, SizeT size, SizeT offset) + ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, uint64 size, uint64 offset) : buf(buf) , slot(slot) , index(0) @@ -291,7 +291,7 @@ struct ResourceTableBuffer , dynamicOffset(false) {}; - ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, SizeT index, SizeT size, SizeT offset, bool texelBuffer = false, bool dynamicOffset = false) + ResourceTableBuffer(const CoreGraphics::BufferId buf, IndexT slot, SizeT index, uint64 size, uint64 offset, bool texelBuffer = false, bool dynamicOffset = false) : buf(buf) , slot(slot) , index(index) @@ -305,8 +305,8 @@ struct ResourceTableBuffer IndexT slot; IndexT index; - SizeT size; - SizeT offset; + uint64 size; + uint64 offset; bool texelBuffer; bool dynamicOffset; @@ -330,6 +330,7 @@ struct ResourceTableTlas struct ResourceTableCreateInfo { + const char* name = nullptr; ResourceTableLayoutId layout; uint overallocationSize = 256; }; diff --git a/code/render/coregraphics/shader.h b/code/render/coregraphics/shader.h index 92fe067ed..62aba8fb8 100644 --- a/code/render/coregraphics/shader.h +++ b/code/render/coregraphics/shader.h @@ -95,9 +95,9 @@ const ShaderId ShaderGet(const Resources::ResourceName& name); CoreGraphics::ShaderFeature::Mask ShaderFeatureMask(const Util::String& mask); /// create resource table from shader -const ResourceTableId ShaderCreateResourceTable(const ShaderId id, const IndexT group, const uint overallocationSize = 1); +const ResourceTableId ShaderCreateResourceTable(const ShaderId id, const IndexT group, const uint overallocationSize = 1, const char* name = nullptr); /// create resource table set from shader -ResourceTableSet ShaderCreateResourceTableSet(const ShaderId id, const IndexT group, const uint overallocationSize = 1); +ResourceTableSet ShaderCreateResourceTableSet(const ShaderId id, const IndexT group, const uint overallocationSize = 1, const char* name = nullptr); /// Returns true if there is a resource table for the given group in the shader const bool ShaderHasResourceTable(const ShaderId id, const IndexT group); /// create constant buffer from shader using name (don't use too frequently) diff --git a/code/render/coregraphics/shadersemantics.h b/code/render/coregraphics/shadersemantics.h index 5edd76e92..092264b55 100644 --- a/code/render/coregraphics/shadersemantics.h +++ b/code/render/coregraphics/shadersemantics.h @@ -38,8 +38,6 @@ #define NEBULA_SEMANTIC_GLOBALLIGHTDIRWORLDSPACE "GlobalLightDirWorldspace" #define NEBULA_SEMANTIC_GLOBALLIGHTCOLOR "GlobalLightColor" #define NEBULA_SEMANTIC_GLOBALAMBIENTLIGHTCOLOR "GlobalAmbientLightColor" -#define NEBULA_SEMANTIC_GLOBALBACKLIGHTCOLOR "GlobalBackLightColor" -#define NEBULA_SEMANTIC_GLOBALBACKLIGHTOFFSET "GlobalBackLightOffset" #define NEBULA_SEMANTIC_GLOBALLIGHTSHADOWBIAS "GlobalLightShadowBias" #define NEBULA_SEMANTIC_LIGHTPROJTRANSFORM "LightProjTransform" #define NEBULA_SEMANTIC_LIGHTTRANSFORM "LightTransform" diff --git a/code/render/coregraphics/texture.cc b/code/render/coregraphics/texture.cc index 0a57f43cc..04d337f76 100644 --- a/code/render/coregraphics/texture.cc +++ b/code/render/coregraphics/texture.cc @@ -29,7 +29,7 @@ Util::Array TrackedTextures; //------------------------------------------------------------------------------ /** */ -void +void TextureGenerateMipmaps(const CoreGraphics::CmdBufferId cmdBuf, const TextureId id) { N_CMD_SCOPE(cmdBuf, NEBULA_MARKER_TRANSFER, "Mipmap"); @@ -145,7 +145,7 @@ TextureUpdate(const CoreGraphics::CmdBufferId cmd, CoreGraphics::TextureId tex, //------------------------------------------------------------------------------ /** */ -TextureCreateInfoAdjusted +TextureCreateInfoAdjusted TextureGetAdjustedInfo(const TextureCreateInfo& info) { TextureCreateInfoAdjusted rt; @@ -177,6 +177,7 @@ TextureGetAdjustedInfo(const TextureCreateInfo& info) rt.window = CoreGraphics::InvalidWindowId; rt.alias = info.alias; rt.defaultLayout = info.defaultLayout; + rt.swizzle = info.swizzle; // correct depth-stencil formats if layout is shader read if (CoreGraphics::PixelFormat::IsDepthFormat(rt.format) && rt.defaultLayout == CoreGraphics::ImageLayout::ShaderRead) diff --git a/code/render/coregraphics/texture.h b/code/render/coregraphics/texture.h index 590d95868..ea7661821 100644 --- a/code/render/coregraphics/texture.h +++ b/code/render/coregraphics/texture.h @@ -137,7 +137,7 @@ struct TextureCreateInfo SizeT dataSize; CoreGraphics::TextureType type; CoreGraphics::PixelFormat::Code format; - float width, height, depth; + float width, height, depth; // these are float because they might be a scale factor (if window relative) uint mips, minMip, layers; uint samples; bool clear; diff --git a/code/render/coregraphics/vk/vkaccelerationstructure.cc b/code/render/coregraphics/vk/vkaccelerationstructure.cc index 584615ace..3f02398a5 100644 --- a/code/render/coregraphics/vk/vkaccelerationstructure.cc +++ b/code/render/coregraphics/vk/vkaccelerationstructure.cc @@ -57,10 +57,10 @@ BlasGetVkBuild(const CoreGraphics::BlasId id) //------------------------------------------------------------------------------ /** */ -const Util::Array& +const VkAccelerationStructureBuildRangeInfoKHR& BlasGetVkRanges(const CoreGraphics::BlasId id) { - return blasAllocator.ConstGet(id.id).rangeInfos; + return blasAllocator.ConstGet(id.id).primitiveGroup; } //------------------------------------------------------------------------------ @@ -131,7 +131,7 @@ CreateBlas(const BlasCreateInfo& info) constexpr VkBuildAccelerationStructureFlagsKHR Lookup[] = { VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR - , VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR + , VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR , VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR , VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR , VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_KHR @@ -154,7 +154,7 @@ CreateBlas(const BlasCreateInfo& info) vkGetPhysicalDeviceFormatProperties2(Vulkan::GetCurrentPhysicalDevice(), positionsFormat, &formatProps); n_assert(formatProps.formatProperties.bufferFeatures & VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR); - setup.triangleData = + VkAccelerationStructureGeometryTrianglesDataKHR triangleData = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR, .pNext = nullptr, @@ -166,21 +166,15 @@ CreateBlas(const BlasCreateInfo& info) .indexData = VkDeviceOrHostAddressConstKHR {.deviceAddress = iboAddr + info.indexOffset}, .transformData = VkDeviceOrHostAddressConstKHR{ .hostAddress = nullptr } // TODO: Support transforms }; - - VkAccelerationStructureGeometryKHR geometry = + setup.geometry = { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR, .pNext = nullptr, .geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR, - .geometry = VkAccelerationStructureGeometryDataKHR{ .triangles = setup.triangleData }, + .geometry = VkAccelerationStructureGeometryDataKHR{ .triangles = triangleData }, .flags = VK_GEOMETRY_OPAQUE_BIT_KHR // TODO, add support for avoiding anyhit or single-invocation anyhit optimizations }; - // Match the number of geometries to the amount of primitive groups - for (IndexT i = 0; i < info.primitiveGroups.Size(); i++) - { - setup.geometries.Append(geometry); - } setup.buildGeometryInfo = { @@ -191,27 +185,19 @@ CreateBlas(const BlasCreateInfo& info) .mode = VkBuildAccelerationStructureModeKHR::VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR, .srcAccelerationStructure = VK_NULL_HANDLE, .dstAccelerationStructure = VK_NULL_HANDLE, - .geometryCount = (uint)info.primitiveGroups.Size(), - .pGeometries = setup.geometries.Begin(), + .geometryCount = 1, + .pGeometries = &setup.geometry, .ppGeometries = nullptr, .scratchData = VkDeviceOrHostAddressKHR{ .hostAddress = nullptr } }; - Util::Array maxPrimitiveCounts; - - // Each primitive group is an individual range - for (IndexT i = 0; i < info.primitiveGroups.Size(); i++) - { - uint primitiveCount = info.primitiveGroups[i].GetNumPrimitives(CoreGraphics::PrimitiveTopology::TriangleList); - setup.rangeInfos.Append( - { - .primitiveCount = (uint)primitiveCount, - .primitiveOffset = 0, // Primitive offset is defined in the mesh - .firstVertex = (uint)info.primitiveGroups[i].GetBaseIndex(), - .transformOffset = 0 - }); - maxPrimitiveCounts.Append(primitiveCount); - } + setup.primitiveGroup = { + .primitiveCount = (uint)info.primGroup.GetNumPrimitives(CoreGraphics::PrimitiveTopology::TriangleList), + .primitiveOffset = (uint)info.primGroup.GetBaseIndex() * CoreGraphics::IndexType::SizeOf(info.indexType), // Primitive offset is defined in the mesh + .firstVertex = 0, + .transformOffset = 0 + }; + uint maxPrimitiveCount = setup.primitiveGroup.primitiveCount; // Get build sizes setup.buildSizes = @@ -220,13 +206,13 @@ CreateBlas(const BlasCreateInfo& info) nullptr, 0, 0, 0 }; - vkGetAccelerationStructureBuildSizesKHR(dev, type, &setup.buildGeometryInfo, maxPrimitiveCounts.Begin(), &setup.buildSizes); + vkGetAccelerationStructureBuildSizesKHR(dev, type, &setup.buildGeometryInfo, &maxPrimitiveCount, &setup.buildSizes); CoreGraphics::BufferCreateInfo bufferInfo; bufferInfo.byteSize = setup.buildSizes.accelerationStructureSize; bufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; bufferInfo.usageFlags = CoreGraphics::BufferUsageFlag::ShaderAddress | CoreGraphics::BufferUsageFlag::AccelerationStructureData; - bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport; + bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; // Create main buffer CoreGraphics::BufferId blasBuf = CoreGraphics::CreateBuffer(bufferInfo); @@ -453,7 +439,7 @@ CreateTlas(const TlasCreateInfo& info) bufferInfo.byteSize = scene.buildSizes.accelerationStructureSize; bufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; bufferInfo.usageFlags = CoreGraphics::BufferUsageFlag::ShaderAddress | CoreGraphics::BufferUsageFlag::AccelerationStructureData; - bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport; + bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; // Create main buffer tlasBuf = CoreGraphics::CreateBuffer(bufferInfo); @@ -466,7 +452,7 @@ CreateTlas(const TlasCreateInfo& info) bufferInfo.byteSize = scene.buildSizes.buildScratchSize; bufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; bufferInfo.usageFlags = CoreGraphics::BufferUsageFlag::ShaderAddress | CoreGraphics::BufferUsageFlag::ReadWriteBuffer | CoreGraphics::BufferUsageFlag::AccelerationStructureScratch; - bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport; + bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; buildScratchBuf = CoreGraphics::CreateBuffer(bufferInfo); tlasAllocator.Set(id, buildScratchBuf); @@ -486,7 +472,7 @@ CreateTlas(const TlasCreateInfo& info) bufferInfo.byteSize = scene.buildSizes.updateScratchSize; bufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; bufferInfo.usageFlags = CoreGraphics::BufferUsageFlag::ShaderAddress | CoreGraphics::BufferUsageFlag::ReadWriteBuffer | CoreGraphics::BufferUsageFlag::AccelerationStructureScratch; - bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport; + bufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; updateScratchBuf = CoreGraphics::CreateBuffer(bufferInfo); tlasAllocator.Set(id, updateScratchBuf); diff --git a/code/render/coregraphics/vk/vkaccelerationstructure.h b/code/render/coregraphics/vk/vkaccelerationstructure.h index 9b613a94c..28a78cc0d 100644 --- a/code/render/coregraphics/vk/vkaccelerationstructure.h +++ b/code/render/coregraphics/vk/vkaccelerationstructure.h @@ -14,11 +14,10 @@ namespace Vulkan struct GeometrySetup { - VkAccelerationStructureGeometryTrianglesDataKHR triangleData; VkAccelerationStructureBuildSizesInfoKHR buildSizes; VkAccelerationStructureBuildGeometryInfoKHR buildGeometryInfo; - Util::Array geometries; - Util::Array rangeInfos; + VkAccelerationStructureGeometryKHR geometry; + VkAccelerationStructureBuildRangeInfoKHR primitiveGroup; }; enum @@ -51,7 +50,7 @@ const VkAccelerationStructureKHR BlasGetVk(const CoreGraphics::BlasId id); /// Get build info for bottom level acceleration structure const VkAccelerationStructureBuildGeometryInfoKHR& BlasGetVkBuild(const CoreGraphics::BlasId id); /// Get range infos for bottom level acceleration structure -const Util::Array& BlasGetVkRanges(const CoreGraphics::BlasId id); +const VkAccelerationStructureBuildRangeInfoKHR& BlasGetVkRanges(const CoreGraphics::BlasId id); struct InstanceSetup { diff --git a/code/render/coregraphics/vk/vkbuffer.cc b/code/render/coregraphics/vk/vkbuffer.cc index 12b24a57d..027270b80 100644 --- a/code/render/coregraphics/vk/vkbuffer.cc +++ b/code/render/coregraphics/vk/vkbuffer.cc @@ -14,7 +14,7 @@ VkBufferSparseExtensionAllocator bufferSparseExtensionAllocator; //------------------------------------------------------------------------------ /** */ -VkBuffer +VkBuffer BufferGetVk(const CoreGraphics::BufferId id) { return bufferAllocator.ConstGet(id.id).buf; @@ -23,7 +23,7 @@ BufferGetVk(const CoreGraphics::BufferId id) //------------------------------------------------------------------------------ /** */ -VkDeviceMemory +VkDeviceMemory BufferGetVkMemory(const CoreGraphics::BufferId id) { return bufferAllocator.ConstGet(id.id).mem.mem; @@ -32,7 +32,7 @@ BufferGetVkMemory(const CoreGraphics::BufferId id) //------------------------------------------------------------------------------ /** */ -VkDevice +VkDevice BufferGetVkDevice(const CoreGraphics::BufferId id) { return bufferAllocator.ConstGet(id.id).dev; @@ -48,7 +48,7 @@ _IMPL_ACQUIRE_RELEASE(BufferId, bufferAllocator); //------------------------------------------------------------------------------ /** */ -const BufferId +const BufferId CreateBuffer(const BufferCreateInfo& info) { Ids::Id32 id = bufferAllocator.Alloc(); @@ -277,12 +277,12 @@ CreateBuffer(const BufferCreateInfo& info) //------------------------------------------------------------------------------ /** */ -void +void DestroyBuffer(const BufferId id) { __Lock(bufferAllocator, id.id); VkBufferLoadInfo& loadInfo = bufferAllocator.Get(id.id); - + CoreGraphics::DelayedDeleteBuffer(id); CoreGraphics::DelayedFreeMemory(loadInfo.mem); loadInfo.mem = CoreGraphics::Alloc{}; @@ -292,7 +292,7 @@ DestroyBuffer(const BufferId id) //------------------------------------------------------------------------------ /** */ -const BufferUsageFlags +const BufferUsageFlags BufferGetType(const BufferId id) { return bufferAllocator.ConstGet(id.id).usageFlags; @@ -301,7 +301,7 @@ BufferGetType(const BufferId id) //------------------------------------------------------------------------------ /** */ -const SizeT +const uint64 BufferGetSize(const BufferId id) { return bufferAllocator.ConstGet(id.id).size; @@ -310,7 +310,7 @@ BufferGetSize(const BufferId id) //------------------------------------------------------------------------------ /** */ -const SizeT +const uint64 BufferGetElementSize(const BufferId id) { return bufferAllocator.ConstGet(id.id).elementSize; @@ -319,7 +319,7 @@ BufferGetElementSize(const BufferId id) //------------------------------------------------------------------------------ /** */ -const SizeT +const uint64 BufferGetByteSize(const BufferId id) { return bufferAllocator.ConstGet(id.id).byteSize; @@ -328,7 +328,7 @@ BufferGetByteSize(const BufferId id) //------------------------------------------------------------------------------ /** */ -const SizeT +const uint64 BufferGetUploadMaxSize() { return 65536; @@ -337,7 +337,7 @@ BufferGetUploadMaxSize() //------------------------------------------------------------------------------ /** */ -void* +void* BufferMap(const BufferId id) { const VkBufferMapInfo& mapInfo = bufferAllocator.ConstGet(id.id); @@ -348,7 +348,7 @@ BufferMap(const BufferId id) //------------------------------------------------------------------------------ /** */ -void +void BufferUnmap(const BufferId id) { // Do nothing on Vulkan @@ -387,8 +387,8 @@ void BufferFill(const CoreGraphics::CmdBufferId cmdBuf, const BufferId id, char pattern) { const VkBufferLoadInfo& setup = bufferAllocator.ConstGet(id.id); - - int remainingBytes = setup.byteSize; + + uint64 remainingBytes = setup.byteSize; uint numChunks = Math::divandroundup(setup.byteSize, BufferGetUploadMaxSize()); int chunkOffset = 0; for (uint i = 0; i < numChunks; i++) @@ -406,8 +406,8 @@ BufferFill(const CoreGraphics::CmdBufferId cmdBuf, const BufferId id, char patte //------------------------------------------------------------------------------ /** */ -void -BufferFlush(const BufferId id, IndexT offset, SizeT size) +void +BufferFlush(const BufferId id, uint64 offset, uint64 size) { const VkBufferLoadInfo& loadInfo = bufferAllocator.ConstGet(id.id); n_assert(size == NEBULA_WHOLE_BUFFER_SIZE ? true : (uint)offset + size <= loadInfo.byteSize); @@ -417,8 +417,8 @@ BufferFlush(const BufferId id, IndexT offset, SizeT size) //------------------------------------------------------------------------------ /** */ -void -BufferInvalidate(const BufferId id, IndexT offset, SizeT size) +void +BufferInvalidate(const BufferId id, uint64 offset, uint64 size) { const VkBufferLoadInfo& loadInfo = bufferAllocator.ConstGet(id.id); n_assert(size == NEBULA_WHOLE_BUFFER_SIZE ? true : (uint)offset + size <= loadInfo.byteSize); diff --git a/code/render/coregraphics/vk/vkbuffer.h b/code/render/coregraphics/vk/vkbuffer.h index f69bebdc0..6084bccd4 100644 --- a/code/render/coregraphics/vk/vkbuffer.h +++ b/code/render/coregraphics/vk/vkbuffer.h @@ -20,9 +20,9 @@ struct VkBufferLoadInfo VkDevice dev; CoreGraphics::Alloc mem; CoreGraphics::BufferAccessMode mode; - uint32_t size; - uint32_t elementSize; - uint32_t byteSize; + uint64 size; + uint64 elementSize; + uint64 byteSize; Ids::Id32 sparseExtension; }; diff --git a/code/render/coregraphics/vk/vkcommandbuffer.cc b/code/render/coregraphics/vk/vkcommandbuffer.cc index 10cc8dfe1..3305a0712 100644 --- a/code/render/coregraphics/vk/vkcommandbuffer.cc +++ b/code/render/coregraphics/vk/vkcommandbuffer.cc @@ -523,12 +523,9 @@ CmdSetShaderProgram(const CmdBufferId id, const CoreGraphics::ShaderProgramId pr /** */ void -CmdSetResourceTable(const CmdBufferId id, const CoreGraphics::ResourceTableId table, const IndexT slot, CoreGraphics::ShaderPipeline pipeline, const Util::FixedArray& offsets) +CmdSetResourceTable(const CmdBufferId id, const CoreGraphics::ResourceTableId table, const IndexT slot, CoreGraphics::ShaderPipeline pipeline, const Util::FixedArray& offsets) { - if (offsets.IsEmpty()) - CmdSetResourceTable(id, table, slot, pipeline, 0, nullptr); - else - CmdSetResourceTable(id, table, slot, pipeline, offsets.Size(), offsets.Begin()); + CmdSetResourceTable(id, table, slot, pipeline, offsets.Size(), offsets.Begin()); } //------------------------------------------------------------------------------ @@ -679,6 +676,7 @@ CmdSetRayTracingPipeline(const CmdBufferId buf, const PipelineId pipeline) VkPipelineBundle& pipelineBundle = commandBuffers.Get(buf.id); Pipeline& pipelineObj = pipelineAllocator.Get(pipeline.id); pipelineBundle.raytracingLayout = pipelineObj.layout; + vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineObj.pipeline); bool pipelineChange = pipelineBundle.graphicsLayout != pipelineObj.layout; pipelineBundle.graphicsLayout = pipelineObj.layout; @@ -688,8 +686,6 @@ CmdSetRayTracingPipeline(const CmdBufferId buf, const PipelineId pipeline) CoreGraphics::CmdSetResourceTable(buf, Graphics::GetTickResourceTable(buffer), NEBULA_TICK_GROUP, CoreGraphics::ShaderPipeline::RayTracingPipeline, nullptr); CoreGraphics::CmdSetResourceTable(buf, Graphics::GetFrameResourceTable(buffer), NEBULA_FRAME_GROUP, CoreGraphics::ShaderPipeline::RayTracingPipeline, nullptr); } - - vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipelineObj.pipeline); } //------------------------------------------------------------------------------ @@ -713,7 +709,7 @@ CmdBarrier( VkPipelineStageFlags toFlags = VkTypes::AsVkPipelineStage(toStage); VkDependencyFlags depFlags = domain == CoreGraphics::BarrierDomain::Pass ? VK_DEPENDENCY_BY_REGION_BIT : 0; uint numBuffers = buffers.Size() + accelerationStructures.Size(); - VkBufferMemoryBarrier* bufferBarriers = (VkBufferMemoryBarrier*)StackAlloc(numBuffers * sizeof(VkBufferMemoryBarrier)); + VkBufferMemoryBarrier* bufferBarriers = ArrayAllocStack(numBuffers); IndexT i, j = 0; for (i = 0; i < buffers.Size(); i++, j++) @@ -758,7 +754,7 @@ CmdBarrier( } uint numImages = textures.Size(); - VkImageMemoryBarrier* imageBarriers = (VkImageMemoryBarrier*)StackAlloc(textures.Size() * sizeof(VkImageMemoryBarrier)); + VkImageMemoryBarrier* imageBarriers = ArrayAllocStack(textures.Size()); for (i = 0; i < textures.Size(); i++, j++) { VkImageMemoryBarrier& vkBar = imageBarriers[j]; @@ -808,8 +804,8 @@ CmdBarrier( numBuffers, bufferBarriers, numImages, imageBarriers); - StackFree(bufferBarriers); - StackFree(imageBarriers); + ArrayFreeStack(textures.size(), imageBarriers); + ArrayFreeStack(numBuffers, bufferBarriers); } //------------------------------------------------------------------------------ @@ -831,7 +827,7 @@ CmdHandover( VkPipelineStageFlags fromFlags = VkTypes::AsVkPipelineStage(fromStage); VkPipelineStageFlags toFlags = VkTypes::AsVkPipelineStage(toStage); uint numBuffers = buffers.Size(); - VkBufferMemoryBarrier* bufferBarriers = (VkBufferMemoryBarrier*)StackAlloc(numBuffers * sizeof(VkBufferMemoryBarrier)); + VkBufferMemoryBarrier* bufferBarriers = ArrayAllocStack(numBuffers); for (uint32_t i = 0; i < numBuffers; i++) { @@ -851,7 +847,7 @@ CmdHandover( } uint numImages = textures.Size(); - VkImageMemoryBarrier* imageBarriers = (VkImageMemoryBarrier*)StackAlloc(textures.Size() * sizeof(VkImageMemoryBarrier)); + VkImageMemoryBarrier* imageBarriers = ArrayAllocStack(textures.Size()); IndexT i, j = 0; for (i = 0; i < textures.Size(); i++, j++) { @@ -944,8 +940,8 @@ CmdHandover( , numImages, imageBarriers ); - StackFree(imageBarriers); - StackFree(bufferBarriers); + ArrayFreeStack(textures.Size(), imageBarriers); + ArrayFreeStack(numBuffers, bufferBarriers); } //------------------------------------------------------------------------------ @@ -1162,8 +1158,8 @@ CmdBuildBlas(const CmdBufferId id, const CoreGraphics::BlasId blas) { VkCommandBuffer cmdBuf = commandBuffers.Get(id.id); const VkAccelerationStructureBuildGeometryInfoKHR& buildInfo = Vulkan::BlasGetVkBuild(blas); - const Util::Array& rangeInfo = Vulkan::BlasGetVkRanges(blas); - const VkAccelerationStructureBuildRangeInfoKHR* ranges[] = { rangeInfo.ConstBegin() }; + const VkAccelerationStructureBuildRangeInfoKHR& rangeInfo = Vulkan::BlasGetVkRanges(blas); + const VkAccelerationStructureBuildRangeInfoKHR* ranges[] = { &rangeInfo }; vkCmdBuildAccelerationStructuresKHR(cmdBuf, 1, &buildInfo, ranges); } @@ -1613,10 +1609,11 @@ CmdBeginMarker(const CmdBufferId id, const Math::vec4& color, const char* name) Util::Array& checkpoints = commandBuffers.Get(id.id); if (CoreGraphics::NvidiaCheckpointsSupported) { + NvidiaAftermathCheckpoint* prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); NvidiaAftermathCheckpoint& checkpoint = checkpoints.Emplace(); checkpoint.name = name; checkpoint.push = 1; - checkpoint.prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); + checkpoint.prev = prev; vkCmdSetCheckpointNV(cmdBuf, &checkpoint); } #endif @@ -1655,10 +1652,11 @@ CmdEndMarker(const CmdBufferId id) Util::Array& checkpoints = commandBuffers.Get(id.id); if (CoreGraphics::NvidiaCheckpointsSupported) { + NvidiaAftermathCheckpoint* prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); NvidiaAftermathCheckpoint& checkpoint = checkpoints.Emplace(); checkpoint.name = nullptr; checkpoint.push = 0; - checkpoint.prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); + checkpoint.prev = prev; vkCmdSetCheckpointNV(cmdBuf, &checkpoint); } #endif @@ -1687,10 +1685,11 @@ CmdInsertMarker(const CmdBufferId id, const Math::vec4& color, const char* name) Util::Array& checkpoints = commandBuffers.Get(id.id); if (CoreGraphics::NvidiaCheckpointsSupported) { + NvidiaAftermathCheckpoint* prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); NvidiaAftermathCheckpoint& checkpoint = checkpoints.Emplace(); checkpoint.name = name; checkpoint.push = 0; - checkpoint.prev = checkpoints.IsEmpty() ? nullptr : &checkpoints.Back(); + checkpoint.prev = prev; vkCmdSetCheckpointNV(cmdBuf, &checkpoint); } #endif diff --git a/code/render/coregraphics/vk/vkcommandbuffer.h b/code/render/coregraphics/vk/vkcommandbuffer.h index 8e2615f3d..9dda9f849 100644 --- a/code/render/coregraphics/vk/vkcommandbuffer.h +++ b/code/render/coregraphics/vk/vkcommandbuffer.h @@ -27,7 +27,7 @@ namespace Vulkan #if NEBULA_GRAPHICS_DEBUG struct NvidiaAftermathCheckpoint { - Util::String name; + Util::StringAtom name; NvidiaAftermathCheckpoint* prev; bool push : 1; }; diff --git a/code/render/coregraphics/vk/vkgraphicsdevice.cc b/code/render/coregraphics/vk/vkgraphicsdevice.cc index 650f2db0d..a19c6a87b 100644 --- a/code/render/coregraphics/vk/vkgraphicsdevice.cc +++ b/code/render/coregraphics/vk/vkgraphicsdevice.cc @@ -682,7 +682,7 @@ DeviceLost() if (next->name != nullptr) { n_printf("%*s", indentation * 4, "-"); - n_printf(" %s\n", next->name.AsCharPtr()); + n_printf(" %s\n", next->name.Value()); } if (next->push) @@ -1791,6 +1791,8 @@ SubmitImmediateCommandBuffers() graphicsWait.timelineIndex = state.queueHandler.AppendSubmissionTimeline(CoreGraphics::GraphicsQueueType, vkBufs, { handoverWait }, "Setup"); graphicsWait.queue = CoreGraphics::GraphicsQueueType; + state.queueHandler.AppendSubmissionTimeline(CoreGraphics::ComputeQueueType, nullptr, {graphicsWait}, "Wait for Setup"); + // Add wait event AddSubmissionEvent(graphicsWait); diff --git a/code/render/coregraphics/vk/vkpass.cc b/code/render/coregraphics/vk/vkpass.cc index f8e7ba665..a5d650cb5 100644 --- a/code/render/coregraphics/vk/vkpass.cc +++ b/code/render/coregraphics/vk/vkpass.cc @@ -579,7 +579,7 @@ SetupPass(const PassId pid) runtimeInfo.renderTargetDimensionsVar = ShaderGetConstantBinding(sid, "RenderTargetParameter"); CoreGraphics::ResourceTableLayoutId tableLayout = ShaderGetResourceTableLayout(sid, NEBULA_PASS_GROUP); - runtimeInfo.passDescriptorSet = CreateResourceTable(ResourceTableCreateInfo{ tableLayout, 8 }); + runtimeInfo.passDescriptorSet = CreateResourceTable(ResourceTableCreateInfo{ Util::String::Sprintf("Pass %s Descriptors", loadInfo.name.Value()).AsCharPtr(), tableLayout, 8 }); runtimeInfo.passPipelineLayout = ShaderGetResourcePipeline(sid); CoreGraphics::ResourceTableBuffer write; diff --git a/code/render/coregraphics/vk/vkpipeline.cc b/code/render/coregraphics/vk/vkpipeline.cc index d5cdd4ea3..ddbebcbdc 100644 --- a/code/render/coregraphics/vk/vkpipeline.cc +++ b/code/render/coregraphics/vk/vkpipeline.cc @@ -68,7 +68,7 @@ CreateGraphicsPipeline(const PipelineCreateInfo& info) shaderInfo.stageCount = programInfo.stageCount; shaderInfo.pStages = programInfo.graphicsShaderInfos; - // Since this is the public facing API, we have to convert the primitive type as it's expected to be in CoreGraphics + // Since this is the public facing API, we have to convert the primitive type as it's expected to be in CoreGraphics InputAssemblyKey translatedKey; translatedKey.topo = Vulkan::VkTypes::AsVkPrimitiveType((CoreGraphics::PrimitiveTopology::Code)info.inputAssembly.topo); translatedKey.primRestart = info.inputAssembly.primRestart; @@ -97,7 +97,7 @@ DestroyGraphicsPipeline(const PipelineId pipeline) /** */ const PipelineRayTracingTable -CreateRaytracingPipeline(const Util::Array programs) +CreateRaytracingPipeline(const Util::Array programs, const CoreGraphics::QueueType queueType) { PipelineRayTracingTable ret; VkDevice dev = CoreGraphics::GetCurrentDevice(); @@ -363,7 +363,7 @@ CreateRaytracingPipeline(const Util::Array progra Memory::Free(Memory::ResourceHeap, buf); - auto CreateShaderTableBuffer = [](const Util::Array& data, RayDispatchTable::Entry& tableEntry) -> CoreGraphics::BufferId + auto CreateShaderTableBuffer = [queueType](const Util::Array& data, RayDispatchTable::Entry& tableEntry) -> CoreGraphics::BufferId { tableEntry.baseAddress = 0xFFFFFFFF; tableEntry.numEntries = 0; @@ -374,7 +374,7 @@ CreateRaytracingPipeline(const Util::Array progra CoreGraphics::BufferCreateInfo tableInfo; tableInfo.byteSize = data.Size(); tableInfo.usageFlags = CoreGraphics::BufferUsageFlag::ShaderTable | CoreGraphics::BufferUsageFlag::ShaderAddress; - tableInfo.queueSupport = CoreGraphics::BufferQueueSupport::GraphicsQueueSupport; + tableInfo.queueSupport = queueType == CoreGraphics::ComputeQueueType ? CoreGraphics::BufferQueueSupport::ComputeQueueSupport : CoreGraphics::BufferQueueSupport::GraphicsQueueSupport; tableInfo.mode = CoreGraphics::BufferAccessMode::HostLocal; tableInfo.data = data.Begin(); tableInfo.dataSize = data.Size(); diff --git a/code/render/coregraphics/vk/vkresourcetable.cc b/code/render/coregraphics/vk/vkresourcetable.cc index 6cd976097..d9fc4f294 100644 --- a/code/render/coregraphics/vk/vkresourcetable.cc +++ b/code/render/coregraphics/vk/vkresourcetable.cc @@ -54,7 +54,7 @@ ResourceTableGetVkDevice(CoreGraphics::ResourceTableId id) void SetupEmptyDescriptorSetLayout() { - VkDescriptorSetLayoutCreateInfo info = + VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, @@ -222,6 +222,8 @@ CreateResourceTable(const ResourceTableCreateInfo& info) ResourceTableLayoutAllocTable(layout, dev, info.overallocationSize, poolIndex, set); ResourceTableId ret = id; + if (info.name != nullptr) + ObjectSetName(ret, info.name); return ret; } @@ -334,7 +336,7 @@ ResourceTableSetTexture(const ResourceTableId id, const ResourceTableTexture& te //------------------------------------------------------------------------------ /** */ -void +void ResourceTableSetTexture(const ResourceTableId id, const ResourceTableTextureView& tex) { VkDescriptorSet& set = resourceTableAllocator.Get(id.id); @@ -469,7 +471,7 @@ ResourceTableSetRWTexture(const ResourceTableId id, const ResourceTableTexture& //------------------------------------------------------------------------------ /** */ -void +void ResourceTableSetRWTexture(const ResourceTableId id, const ResourceTableTextureView& tex) { VkDescriptorSet& set = resourceTableAllocator.Get(id.id); @@ -509,7 +511,7 @@ ResourceTableSetRWTexture(const ResourceTableId id, const ResourceTableTextureVi //------------------------------------------------------------------------------ /** */ -void +void ResourceTableSetConstantBuffer(const ResourceTableId id, const ResourceTableBuffer& buf) { n_assert(!buf.texelBuffer); @@ -545,7 +547,7 @@ ResourceTableSetConstantBuffer(const ResourceTableId id, const ResourceTableBuff else buff.buffer = BufferGetVk(buf.buf); buff.offset = buf.offset; - buff.range = buf.size == NEBULA_WHOLE_BUFFER_SIZE ? VK_WHOLE_SIZE : buf.size; + buff.range = buf.size == NEBULA_WHOLE_BUFFER_SIZE ? Math::min(BufferGetByteSize(buf.buf), CoreGraphics::MaxConstantBufferSize) : buf.size; WriteInfo inf; inf.buf = buff; @@ -557,7 +559,7 @@ ResourceTableSetConstantBuffer(const ResourceTableId id, const ResourceTableBuff //------------------------------------------------------------------------------ /** */ -void +void ResourceTableSetRWBuffer(const ResourceTableId id, const ResourceTableBuffer& buf) { VkDescriptorSet& set = resourceTableAllocator.Get(id.id); @@ -675,7 +677,7 @@ ResourceTableSetAccelerationStructure(const ResourceTableId id, const ResourceTa //------------------------------------------------------------------------------ /** */ -void +void ResourceTableBlock(bool b) { ResourceTableBlocked = b; @@ -929,7 +931,7 @@ CreateResourceTableLayout(const ResourceTableLayoutCreateInfo& info) RW buffers */ //------------------------------------------------------------------------------ - + VkDescriptorPoolSize rwBufferSize, rwDynamicBufferSize; rwBufferSize.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; rwBufferSize.descriptorCount = 0; @@ -990,7 +992,7 @@ CreateResourceTableLayout(const ResourceTableLayoutCreateInfo& info) Samplers */ //------------------------------------------------------------------------------ - + VkDescriptorPoolSize samplerSize; samplerSize.type = VK_DESCRIPTOR_TYPE_SAMPLER; samplerSize.descriptorCount = 0; @@ -1021,7 +1023,7 @@ CreateResourceTableLayout(const ResourceTableLayoutCreateInfo& info) Input attachments */ //------------------------------------------------------------------------------ - + VkDescriptorPoolSize inputAttachmentSize; inputAttachmentSize.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; inputAttachmentSize.descriptorCount = 0; diff --git a/code/render/coregraphics/vk/vkshader.cc b/code/render/coregraphics/vk/vkshader.cc index 1ec268167..5fe2bf022 100644 --- a/code/render/coregraphics/vk/vkshader.cc +++ b/code/render/coregraphics/vk/vkshader.cc @@ -95,7 +95,7 @@ ShaderSetup( uint32_t numsets = 0; // always create push constant range in layout, making all shaders using push constants compatible - uint32_t maxConstantBytes = CoreGraphics::MaxPushConstantSize; + uint32_t maxPushConstantBytes = CoreGraphics::MaxPushConstantSize; uint32_t pushRangeOffset = 0; // we must append previous push range size to offset constantRange.Resize(NumShaders); // one per shader stage uint i; @@ -142,9 +142,8 @@ ShaderSetup( if (block->variables.empty()) continue; if (AnyFX::HasFlags(block->qualifiers, AnyFX::Qualifiers::Push)) { - n_assert(block->alignedSize <= maxConstantBytes); - n_assert(block->alignedSize <= CoreGraphics::MaxPushConstantSize); - maxConstantBytes -= block->alignedSize; + n_assert(block->alignedSize <= maxPushConstantBytes); + maxPushConstantBytes -= block->alignedSize; CoreGraphics::ResourcePipelinePushConstantRange range; range.offset = pushRangeOffset; range.size = block->alignedSize; @@ -762,7 +761,7 @@ ShaderGetShaderProgram(const CoreGraphics::ShaderId shaderId, const CoreGraphics /** */ const CoreGraphics::ResourceTableId -ShaderCreateResourceTable(const CoreGraphics::ShaderId id, const IndexT group, const uint overallocationSize) +ShaderCreateResourceTable(const CoreGraphics::ShaderId id, const IndexT group, const uint overallocationSize, const char* name) { const VkShaderSetupInfo& info = shaderAlloc.Get(id.id); IndexT idx = info.descriptorSetLayoutMap.FindIndex(group); @@ -771,8 +770,9 @@ ShaderCreateResourceTable(const CoreGraphics::ShaderId id, const IndexT group, c { ResourceTableCreateInfo crInfo = { - Util::Get<1>(info.descriptorSetLayouts[info.descriptorSetLayoutMap.ValueAtIndex(idx)]), - overallocationSize + .name = name, + .layout = Util::Get<1>(info.descriptorSetLayouts[info.descriptorSetLayoutMap.ValueAtIndex(idx)]), + .overallocationSize = overallocationSize }; return CoreGraphics::CreateResourceTable(crInfo); } @@ -782,7 +782,7 @@ ShaderCreateResourceTable(const CoreGraphics::ShaderId id, const IndexT group, c /** */ ResourceTableSet -ShaderCreateResourceTableSet(const ShaderId id, const IndexT group, const uint overallocationSize) +ShaderCreateResourceTableSet(const ShaderId id, const IndexT group, const uint overallocationSize, const char* name) { const VkShaderSetupInfo& info = shaderAlloc.Get(id.id); IndexT idx = info.descriptorSetLayoutMap.FindIndex(group); @@ -791,8 +791,9 @@ ShaderCreateResourceTableSet(const ShaderId id, const IndexT group, const uint o { ResourceTableCreateInfo crInfo = { - Util::Get<1>(info.descriptorSetLayouts[info.descriptorSetLayoutMap.ValueAtIndex(idx)]), - overallocationSize + .name = name, + .layout = Util::Get<1>(info.descriptorSetLayouts[info.descriptorSetLayoutMap.ValueAtIndex(idx)]), + .overallocationSize = overallocationSize }; return CoreGraphics::ResourceTableSet(crInfo); } diff --git a/code/render/coregraphics/vk/vksubcontexthandler.cc b/code/render/coregraphics/vk/vksubcontexthandler.cc index 50b5dba78..a7cc91550 100644 --- a/code/render/coregraphics/vk/vksubcontexthandler.cc +++ b/code/render/coregraphics/vk/vksubcontexthandler.cc @@ -85,7 +85,7 @@ VkSubContextHandler::Setup(VkDevice dev, const Util::FixedArraydevice, &inf, nullptr, &this->semaphores[i][j]); n_assert(res == VK_SUCCESS); this->semaphoreSubmissionIds[i][j] = 0; - + #if NEBULA_GRAPHICS_DEBUG VkDebugUtilsObjectNameInfoEXT info = { @@ -127,7 +127,7 @@ VkSubContextHandler::SetToNextContext(const CoreGraphics::QueueType type) //------------------------------------------------------------------------------ /** */ -uint64 +uint64 VkSubContextHandler::AppendSubmissionTimeline( CoreGraphics::QueueType type , VkCommandBuffer cmds @@ -137,14 +137,14 @@ VkSubContextHandler::AppendSubmissionTimeline( #endif ) { - n_assert(cmds != VK_NULL_HANDLE); Threading::CriticalScope _0(&this->submissionLock); uint64 ret = GetNextTimelineIndex(type); Util::Array& submissionsForQueue = this->submissions[type]; TimelineSubmission2& sub = submissionsForQueue.Emplace(); - sub.buffers.Append(cmds); + if (cmds != VK_NULL_HANDLE) + sub.buffers.Append(cmds); sub.signalSemaphores.Append(this->semaphores[type][this->currentQueue[type]]); sub.signalIndices.Append(ret); sub.queue = type; @@ -164,7 +164,7 @@ VkSubContextHandler::AppendSubmissionTimeline( // Progress the semaphore counter this->semaphoreSubmissionIds[type][this->currentQueue[type]] = ret; - + return ret; } @@ -207,7 +207,7 @@ VkSubContextHandler::AppendSubmissionTimeline( // Progress the semaphore counter this->semaphoreSubmissionIds[type][this->currentQueue[type]] = ret; - + return ret; } @@ -223,7 +223,7 @@ VkSubContextHandler::GetNextTimelineIndex(CoreGraphics::QueueType type) //------------------------------------------------------------------------------ /** */ -uint64_t +uint64_t VkSubContextHandler::AppendSparseBind(CoreGraphics::QueueType type, const VkImage img, const Util::Array& opaqueBinds, const Util::Array& pageBinds) { Threading::CriticalScope _0(&this->submissionLock); @@ -394,7 +394,7 @@ VkSubContextHandler::FlushSubmissions(VkFence fence) //------------------------------------------------------------------------------ /** */ -void +void VkSubContextHandler::Wait(CoreGraphics::QueueType type, uint64 index) { // we can't really signal index UINT64_MAX, so skip it @@ -433,7 +433,7 @@ VkSubContextHandler::Poll(CoreGraphics::QueueType type, uint64_t index) //------------------------------------------------------------------------------ /** */ -void +void VkSubContextHandler::FlushSparseBinds(VkFence fence) { // abort early diff --git a/code/render/coregraphics/vk/vktypes.cc b/code/render/coregraphics/vk/vktypes.cc index 393d82177..a7b2a6558 100644 --- a/code/render/coregraphics/vk/vktypes.cc +++ b/code/render/coregraphics/vk/vktypes.cc @@ -46,7 +46,7 @@ VkTypes::AsVkFormat(CoreGraphics::PixelFormat::Code p) case PixelFormat::R16G16B16A16F: return VK_FORMAT_R16G16B16A16_SFLOAT; case PixelFormat::R16G16B16A16: return VK_FORMAT_R16G16B16A16_UINT; case PixelFormat::R11G11B10F: return VK_FORMAT_B10G11R11_UFLOAT_PACK32; - case PixelFormat::R9G9B9E5F: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case PixelFormat::R9G9B9E5F: return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; case PixelFormat::R32F: return VK_FORMAT_R32_SFLOAT; case PixelFormat::R32: return VK_FORMAT_R32_UINT; case PixelFormat::R32G32F: return VK_FORMAT_R32G32_SFLOAT; diff --git a/code/render/decals/decalcontext.cc b/code/render/decals/decalcontext.cc index f09be6096..f864f978c 100644 --- a/code/render/decals/decalcontext.cc +++ b/code/render/decals/decalcontext.cc @@ -96,7 +96,6 @@ DecalContext::Create() rwbInfo.name = "DecalListsStagingBuffer"; rwbInfo.mode = BufferAccessMode::HostLocal; rwbInfo.usageFlags = CoreGraphics::TransferBufferSource; - decalState.stagingClusterDecalsList = BufferSet(rwbInfo); for (IndexT i = 0; i < CoreGraphics::GetNumBufferedFrames(); i++) @@ -336,8 +335,8 @@ DecalContext::UpdateViewDependentResources(const Ptr& view, cons CoreGraphics::ResourceTableId frameResourceTable = Graphics::GetFrameResourceTable(bufferIndex); - uint offset = SetConstants(decalUniforms); - ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::DecalUniforms_SLOT, 0, sizeof(Shared::DecalUniforms), (SizeT)offset }); + uint64 offset = SetConstants(decalUniforms); + ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::DecalUniforms_SLOT, 0, sizeof(Shared::DecalUniforms), offset }); ResourceTableCommitChanges(frameResourceTable); // update list of point lights @@ -353,7 +352,7 @@ DecalContext::UpdateViewDependentResources(const Ptr& view, cons //------------------------------------------------------------------------------ /** - Todo: Right now, we just render a box, + Todo: Right now, we just render a box, but probably we want some type of mesh to illustrate this is a decal, and not some 'empty' object */ diff --git a/code/render/fog/volumetricfogcontext.cc b/code/render/fog/volumetricfogcontext.cc index ae312173d..608296fb2 100644 --- a/code/render/fog/volumetricfogcontext.cc +++ b/code/render/fog/volumetricfogcontext.cc @@ -75,7 +75,7 @@ VolumetricFogContext::~VolumetricFogContext() //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::Create() { __CreateContext(); @@ -225,7 +225,7 @@ VolumetricFogContext::Create() //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::Discard() { } @@ -233,11 +233,11 @@ VolumetricFogContext::Discard() //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetupBoxVolume( - const Graphics::GraphicsEntityId id, + const Graphics::GraphicsEntityId id, const Math::mat4& transform, - const float density, + const float density, const Math::vec3& absorption) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -253,7 +253,7 @@ VolumetricFogContext::SetupBoxVolume( //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetBoxTransform(const Graphics::GraphicsEntityId id, const Math::mat4& transform) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -266,12 +266,12 @@ VolumetricFogContext::SetBoxTransform(const Graphics::GraphicsEntityId id, const //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetupSphereVolume( - const Graphics::GraphicsEntityId id, + const Graphics::GraphicsEntityId id, const Math::vec3& position, - float radius, - const float density, + float radius, + const float density, const Math::vec3& absorption) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -288,7 +288,7 @@ VolumetricFogContext::SetupSphereVolume( //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetSpherePosition(const Graphics::GraphicsEntityId id, const Math::vec3& position) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -301,7 +301,7 @@ VolumetricFogContext::SetSpherePosition(const Graphics::GraphicsEntityId id, con //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetSphereRadius(const Graphics::GraphicsEntityId id, const float radius) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -314,7 +314,7 @@ VolumetricFogContext::SetSphereRadius(const Graphics::GraphicsEntityId id, const //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetTurbidity(const Graphics::GraphicsEntityId id, const float turbidity) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -324,7 +324,7 @@ VolumetricFogContext::SetTurbidity(const Graphics::GraphicsEntityId id, const fl //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetAbsorption(const Graphics::GraphicsEntityId id, const Math::vec3& absorption) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -334,7 +334,7 @@ VolumetricFogContext::SetAbsorption(const Graphics::GraphicsEntityId id, const M //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::UpdateViewDependentResources(const Ptr& view, const Graphics::FrameContext& ctx) { using namespace CoreGraphics; @@ -405,8 +405,8 @@ VolumetricFogContext::UpdateViewDependentResources(const Ptr& vi // get per-view resource tables CoreGraphics::ResourceTableId frameResourceTable = Graphics::GetFrameResourceTable(bufferIndex); - uint offset = SetConstants(fogUniforms); - ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::VolumeFogUniforms_SLOT, 0, sizeof(Shared::VolumeFogUniforms), (SizeT)offset }); + uint64 offset = SetConstants(fogUniforms); + ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::VolumeFogUniforms_SLOT, 0, sizeof(Shared::VolumeFogUniforms), offset }); ResourceTableCommitChanges(frameResourceTable); // setup blur tables @@ -423,7 +423,7 @@ VolumetricFogContext::UpdateViewDependentResources(const Ptr& vi //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::RenderUI(const Graphics::FrameContext& ctx) { if (fogState.showUI) @@ -447,7 +447,7 @@ VolumetricFogContext::RenderUI(const Graphics::FrameContext& ctx) //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetGlobalTurbidity(float f) { fogState.turbidity = f; @@ -456,7 +456,7 @@ VolumetricFogContext::SetGlobalTurbidity(float f) //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetGlobalAbsorption(const Math::vec3& color) { fogState.color = color; @@ -465,7 +465,7 @@ VolumetricFogContext::SetGlobalAbsorption(const Math::vec3& color) //------------------------------------------------------------------------------ /** */ -Math::mat4 +Math::mat4 VolumetricFogContext::GetTransform(const Graphics::GraphicsEntityId id) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -484,7 +484,7 @@ VolumetricFogContext::GetTransform(const Graphics::GraphicsEntityId id) //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::SetTransform(const Graphics::GraphicsEntityId id, const Math::mat4& mat) { const Graphics::ContextEntityId cid = GetContextId(id); @@ -502,7 +502,7 @@ VolumetricFogContext::SetTransform(const Graphics::GraphicsEntityId id, const Ma //------------------------------------------------------------------------------ /** */ -Graphics::ContextEntityId +Graphics::ContextEntityId VolumetricFogContext::Alloc() { return fogGenericVolumeAllocator.Alloc(); @@ -511,7 +511,7 @@ VolumetricFogContext::Alloc() //------------------------------------------------------------------------------ /** */ -void +void VolumetricFogContext::Dealloc(Graphics::ContextEntityId id) { fogGenericVolumeAllocator.Dealloc(id.id); diff --git a/code/render/frame/default.json b/code/render/frame/default.json index 76383160b..ae0abd5d8 100644 --- a/code/render/frame/default.json +++ b/code/render/frame/default.json @@ -6,13 +6,15 @@ ], "ImportBuffers": [ "ClusterBuffer", - "ClusterLightList", + "LightList", "ClusterLightIndexLists", "ClusterDecalList", "ClusterDecalIndexLists", "ClusterFogList", "ClusterFogIndexLists", - "GridLightList", + "ClusterGIList", + "ClusterGIIndexLists", + "GridBuffer", "GridLightIndexLists", "GridDecalList", "GridDecalIndexLists", @@ -223,6 +225,16 @@ "name": "Fog Cull" } }, + { + "subgraph": { + "name": "GI Copy" + } + }, + { + "subgraph": { + "name": "GI Cull" + } + }, { "subgraph": { "name": "Sun Terrain Shadows" @@ -242,6 +254,16 @@ "subgraph": { "name": "Terrain Raytracing Mesh Generate" } + }, + { + "subgraph": { + "name": "DDGI Probe Update" + } + }, + { + "subgraph": { + "name": "DDGI Probe Finalize" + } } ] }, @@ -251,12 +273,7 @@ "queue": "Graphics", "_comment": "Calculate shadow maps and depth prepass", "ops": [ - - { - "subgraph": { - "name": "Raytracing Test" - } - }, + { "subgraph": { "name": "StaticUI" @@ -454,6 +471,11 @@ "batch": { "name": "LightMeshes" } + }, + { + "subgraph": { + "name": "DDGI Debug" + } } ] }, @@ -509,7 +531,11 @@ } }, - + { + "subgraph": { + "name": "Raytracing Test" + } + }, { "subgraph": { "name": "Vegetation Copy Indirect" diff --git a/code/render/gi/ddgicontext.cc b/code/render/gi/ddgicontext.cc index 0c7e95f33..729554864 100644 --- a/code/render/gi/ddgicontext.cc +++ b/code/render/gi/ddgicontext.cc @@ -6,17 +6,105 @@ #include "coregraphics/pipeline.h" #include "ddgicontext.h" +#include + +#include "clustering/clustercontext.h" +#include "coregraphics/meshloader.h" +#include "coregraphics/meshresource.h" +#include "frame/default.h" +#include "graphics/cameracontext.h" +#include "graphics/view.h" +#include "lighting/lightcontext.h" +#include "raytracing/raytracingcontext.h" +#include "coregraphics/shaperenderer.h" + + +#include "gi/shaders/probe_update.h" +#include "gi/shaders/probe_finalize.h" +#include "gi/shaders/gi_volume_cull.h" +#include "gi/shaders/probe_relocate_and_classify.h" +#include "graphics/globalconstants.h" + +#ifndef PUBLIC_BUILD +#include "gi/shaders/probe_debug.h" +#endif +#include + +#include "options.h" +#include "core/cvar.h" + +Core::CVar* g_debug_ddgi = Core::CVarCreate(Core::CVar_Int, "g_debug_ddgi", "0", "Draw DDGI probes"); +Core::CVar* g_debug_ddgi_probe_size = Core::CVarCreate(Core::CVar_Float, "g_debug_ddgi_probe_size", "0.01f", "Set size of DDGI probes"); + using namespace Graphics; namespace GI { -DDGIContext::DDGIVolumeAllocator DDGIContext::allocator; -__ImplementContext(DDGIContext, DDGIContext::allocator) +DDGIContext::DDGIVolumeAllocator DDGIContext::ddgiVolumeAllocator; +__ImplementContext(DDGIContext, DDGIContext::ddgiVolumeAllocator) + +struct UpdateVolume +{ + uint probeCounts[3]; + uint numRays; + struct + { + CoreGraphics::TextureId radianceDistanceTexture; + } probeUpdateOutputs; + struct + { + CoreGraphics::TextureId irradianceTexture, distanceTexture, scrollSpaceTexture; + } volumeBlendOutputs; + + struct + { + CoreGraphics::TextureId offsetsTexture, statesTexture; + } volumeStateOutputs; + CoreGraphics::ResourceTableId updateProbesTable, blendProbesTable, relocateAndClassifyProbesTable; + +#ifndef PUBLIC_BUILD + CoreGraphics::ResourceTableId debugTable; +#endif +}; struct { + CoreGraphics::ShaderId probeUpdateShader; + CoreGraphics::ShaderProgramId probeUpdateProgram; CoreGraphics::PipelineRayTracingTable pipeline; + + CoreGraphics::ShaderId volumeCullShader; + CoreGraphics::ShaderProgramId volumeCullProgram, volumeClusterDebugProgram; + + CoreGraphics::ShaderId probeFinalizeShader; + CoreGraphics::ShaderProgramId probeBlendRadianceProgram, probeBlendDistanceProgram, probeBorderRadianceRowsFixup, probeBorderRadianceColumnsFixup, probeBorderDistanceRowsFixup, probeBorderDistanceColumnsFixup; + + CoreGraphics::ShaderId probesRelocateAndClassifyShader; + CoreGraphics::ShaderProgramId probesRelocateAndClassifyProgram; + + CoreGraphics::ResourceTableSet raytracingTable; + + CoreGraphics::BufferId clusterGIVolumeIndexLists; + CoreGraphics::BufferSet stagingClusterGIVolumeList; + CoreGraphics::BufferId clusterGIVolumeList; + + Util::Array volumesToUpdate; +#ifndef PUBLIC_BUILD + Util::Array volumesToDraw; +#endif + + GiVolumeCull::GIVolume giVolumes[64]; + +#ifndef PUBLIC_BUILD + CoreGraphics::ShaderId debugShader; + CoreGraphics::ShaderProgramId debugProgram; + CoreGraphics::PipelineId debugPipeline; + CoreGraphics::MeshResourceId debugMeshResource; + CoreGraphics::MeshId debugMesh; +#endif + + Timing::Time elapsedTime; } state; @@ -25,8 +113,36 @@ struct */ DDGIContext::DDGIContext() { - auto probeUpdateShader = CoreGraphics::ShaderGet("shd:gi/shaders/probeupdate.fxb"); - auto probeUpdateProgram = CoreGraphics::ShaderGetProgram(probeUpdateShader, CoreGraphics::ShaderFeatureMask("ProbeRayGen")); +} + +//------------------------------------------------------------------------------ +/** +*/ +DDGIContext::~DDGIContext() +{ +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::Create() +{ + if (!CoreGraphics::RayTracingSupported) + return; + + __CreateContext(); +#ifndef PUBLIC_BUILD + __bundle.OnRenderDebug = DDGIContext::OnRenderDebug; + + state.debugMeshResource = Resources::CreateResource("sysmsh:sphere.nvx", "GI", nullptr, nullptr, true, false); + state.debugMesh = CoreGraphics::MeshResourceGetMesh(state.debugMeshResource, 0); +#endif + + Graphics::GraphicsServer::Instance()->RegisterGraphicsContext(&__bundle, &__state); + + state.probeUpdateShader = CoreGraphics::ShaderGet("shd:gi/shaders/probe_update.fxb"); + state.probeUpdateProgram = CoreGraphics::ShaderGetProgram(state.probeUpdateShader, CoreGraphics::ShaderFeatureMask("ProbeRayGen")); auto brdfHitShader = CoreGraphics::ShaderGet("shd:raytracing/shaders/brdfhit.fxb"); auto brdfHitProgram = CoreGraphics::ShaderGetProgram(brdfHitShader, CoreGraphics::ShaderFeatureMask("Hit")); @@ -37,25 +153,712 @@ DDGIContext::DDGIContext() auto particleHitShader = CoreGraphics::ShaderGet("shd:raytracing/shaders/particlehit.fxb"); auto particleHitProgram = CoreGraphics::ShaderGetProgram(particleHitShader, CoreGraphics::ShaderFeatureMask("Hit")); + state.raytracingTable = CoreGraphics::ShaderCreateResourceTableSet(state.probeUpdateShader, NEBULA_BATCH_GROUP, 1); + // Create pipeline, the order of hit programs must match RaytracingContext::ObjectType - state.pipeline = CoreGraphics::CreateRaytracingPipeline({ probeUpdateProgram, brdfHitProgram, bsdfHitProgram, gltfHitProgram, particleHitProgram }); + state.pipeline = CoreGraphics::CreateRaytracingPipeline({ state.probeUpdateProgram, brdfHitProgram, bsdfHitProgram, gltfHitProgram, particleHitProgram }, CoreGraphics::ComputeQueueType); + + state.probeFinalizeShader = CoreGraphics::ShaderGet("shd:gi/shaders/probe_finalize.fxb"); + state.probeBlendRadianceProgram = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeRadiance")); + state.probeBlendDistanceProgram = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeDistance")); + state.probeBorderRadianceRowsFixup = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeBorderRowsRadiance")); + state.probeBorderRadianceColumnsFixup = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeBorderColumnsRadiance")); + state.probeBorderDistanceRowsFixup = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeBorderRowsDistance")); + state.probeBorderDistanceColumnsFixup = CoreGraphics::ShaderGetProgram(state.probeFinalizeShader, CoreGraphics::ShaderFeatureMask("ProbeFinalizeBorderColumnsDistance")); + + state.volumeCullShader = CoreGraphics::ShaderGet("shd:gi/shaders/gi_volume_cull.fxb"); + state.volumeCullProgram = CoreGraphics::ShaderGetProgram(state.volumeCullShader, CoreGraphics::ShaderFeatureMask("Cull")); + state.volumeClusterDebugProgram = CoreGraphics::ShaderGetProgram(state.volumeCullShader, CoreGraphics::ShaderFeatureMask("Debug")); + + state.probesRelocateAndClassifyShader = CoreGraphics::ShaderGet("shd:gi/shaders/probe_relocate_and_classify.fxb"); + state.probesRelocateAndClassifyProgram = CoreGraphics::ShaderGetProgram(state.probesRelocateAndClassifyShader, CoreGraphics::ShaderFeatureMask("ProbeRelocateAndClassify")); + +#ifndef PUBLIC_BUILD + state.debugShader = CoreGraphics::ShaderGet("shd:gi/shaders/probe_debug.fxb"); + state.debugProgram = CoreGraphics::ShaderGetProgram(state.debugShader, CoreGraphics::ShaderFeatureMask("Debug")); +#endif + + CoreGraphics::BufferCreateInfo rwbInfo; + rwbInfo.name = "GIIndexListsBuffer"; + rwbInfo.size = 1; + rwbInfo.elementSize = sizeof(GiVolumeCull::GIIndexLists); + rwbInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; + rwbInfo.usageFlags = CoreGraphics::ReadWriteBuffer | CoreGraphics::TransferBufferDestination; + rwbInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; + state.clusterGIVolumeIndexLists = CreateBuffer(rwbInfo); + + rwbInfo.name = "GIVolumeLists"; + rwbInfo.elementSize = sizeof(GiVolumeCull::GIVolumeLists); + state.clusterGIVolumeList = CreateBuffer(rwbInfo); + + rwbInfo.name = "GIVolumeListsStagingBuffer"; + rwbInfo.mode = CoreGraphics::BufferAccessMode::HostLocal; + rwbInfo.usageFlags = CoreGraphics::TransferBufferSource; + state.stagingClusterGIVolumeList = CoreGraphics::BufferSet(rwbInfo); + + for (IndexT i = 0; i < CoreGraphics::GetNumBufferedFrames(); i++) + { + CoreGraphics::ResourceTableId frameResourceTable = Graphics::GetFrameResourceTable(i); + + ResourceTableSetRWBuffer(frameResourceTable, { state.clusterGIVolumeIndexLists, Shared::Table_Frame::GIIndexLists_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetRWBuffer(frameResourceTable, { state.clusterGIVolumeList, Shared::Table_Frame::GIVolumeLists_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetConstantBuffer(frameResourceTable, { CoreGraphics::GetConstantBuffer(i), Shared::Table_Frame::GIVolumeUniforms_SLOT, 0, sizeof(ProbeFinalize::GIVolumeUniforms), 0 }); + ResourceTableCommitChanges(frameResourceTable); + } + + FrameScript_default::Bind_ClusterGIList(state.clusterGIVolumeList); + FrameScript_default::Bind_ClusterGIIndexLists(state.clusterGIVolumeIndexLists); + FrameScript_default::RegisterSubgraph_GICopy_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { + CoreGraphics::BufferCopy from, to; + from.offset = 0; + to.offset = 0; + CmdCopy(cmdBuf, state.stagingClusterGIVolumeList.buffers[bufferIndex], { from }, state.clusterGIVolumeList, { to }, sizeof(Shared::GIVolumeLists)); + }, { + { FrameScript_default::BufferIndex::ClusterGIList, CoreGraphics::PipelineStage::TransferWrite } + }); + + FrameScript_default::RegisterSubgraph_GICull_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { + + CmdSetShaderProgram(cmdBuf, state.volumeCullProgram); + //CoreGraphics::CmdSetResourceTable(cmdBuf, Raytracing::RaytracingContext::GetLightGridResourceTable(bufferIndex), NEBULA_FRAME_GROUP, CoreGraphics::ComputePipeline, nullptr); + + // Run chunks of 1024 threads at a time + std::array dimensions = Clustering::ClusterContext::GetClusterDimensions(); + + CmdDispatch(cmdBuf, Math::ceil((dimensions[0] * dimensions[1] * dimensions[2]) / 64.0f), 1, 1); + }, { + { FrameScript_default::BufferIndex::ClusterGIList, CoreGraphics::PipelineStage::ComputeShaderRead } + , { FrameScript_default::BufferIndex::ClusterGIIndexLists, CoreGraphics::PipelineStage::ComputeShaderWrite } + , { FrameScript_default::BufferIndex::ClusterBuffer, CoreGraphics::PipelineStage::ComputeShaderRead } + }); + + FrameScript_default::RegisterSubgraph_DDGIProbeUpdate_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { + if (!state.volumesToUpdate.IsEmpty()) + { + CoreGraphics::CmdSetRayTracingPipeline(cmdBuf, state.pipeline.pipeline); + CoreGraphics::CmdSetResourceTable(cmdBuf, Raytracing::RaytracingContext::GetRaytracingTable(bufferIndex), NEBULA_BATCH_GROUP, CoreGraphics::RayTracingPipeline, nullptr); + CoreGraphics::CmdSetResourceTable(cmdBuf, Raytracing::RaytracingContext::GetLightGridResourceTable(bufferIndex), NEBULA_FRAME_GROUP, CoreGraphics::RayTracingPipeline, nullptr); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + if (volumeToUpdate.updateProbesTable != CoreGraphics::InvalidResourceTableId) + { + auto bar = CoreGraphics::TextureBarrierInfo{ .tex = volumeToUpdate.probeUpdateOutputs.radianceDistanceTexture, .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() }; + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::PipelineStage::RayTracingShaderWrite, CoreGraphics::BarrierDomain::Global, {bar}); + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.updateProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::RayTracingPipeline, nullptr); + CoreGraphics::CmdRaysDispatch(cmdBuf, state.pipeline.table, volumeToUpdate.numRays, volumeToUpdate.probeCounts[0] * volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2], 1); + } + } + } + }, { + { FrameScript_default::BufferIndex::GridLightIndexLists, CoreGraphics::PipelineStage::RayTracingShaderRead } + , { FrameScript_default::BufferIndex::GridBuffer, CoreGraphics::PipelineStage::RayTracingShaderRead } + , { FrameScript_default::BufferIndex::RayTracingObjectBindings, CoreGraphics::PipelineStage::RayTracingShaderRead } + } ); + + FrameScript_default::RegisterSubgraph_DDGIProbeFinalize_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { + if (!state.volumesToUpdate.IsEmpty()) + { + Util::FixedArray radianceDistanceTextures(state.volumesToUpdate.Size()); + Util::FixedArray volumeBlendTextures(state.volumesToUpdate.Size() * 3); + + uint it = 0; + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + radianceDistanceTextures[it] = CoreGraphics::TextureBarrierInfo{ .tex = volumeToUpdate.probeUpdateOutputs.radianceDistanceTexture, .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() }; + volumeBlendTextures[it * 3] = CoreGraphics::TextureBarrierInfo{ .tex = volumeToUpdate.volumeBlendOutputs.irradianceTexture, .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer()}; + volumeBlendTextures[it * 3 + 1] = CoreGraphics::TextureBarrierInfo{ .tex = volumeToUpdate.volumeBlendOutputs.distanceTexture, .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer()}; + volumeBlendTextures[it * 3 + 2] = CoreGraphics::TextureBarrierInfo{ .tex = volumeToUpdate.volumeBlendOutputs.scrollSpaceTexture, .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer()}; + } + + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::RayTracingShaderWrite, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::BarrierDomain::Global, radianceDistanceTextures); + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::PipelineStage::ComputeShaderWrite, CoreGraphics::BarrierDomain::Global, volumeBlendTextures); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Blend Radiance"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBlendRadianceProgram); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2], volumeToUpdate.probeCounts[0], 1); + } + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Blend Distance"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBlendDistanceProgram); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2], volumeToUpdate.probeCounts[0], 1); + } + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderWrite, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::BarrierDomain::Global, volumeBlendTextures); + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Radiance Rows Fixup"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBorderRadianceRowsFixup); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + uint probeGridWidth = volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2]; + uint probeGridHeight = volumeToUpdate.probeCounts[0]; + uint irradianceTextureWidth = probeGridWidth * ProbeFinalize::NUM_IRRADIANCE_TEXELS_PER_PROBE; + + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, irradianceTextureWidth, probeGridHeight, 1); + } + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Distance Rows Fixup"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBorderDistanceRowsFixup); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + uint probeGridWidth = volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2]; + uint probeGridHeight = volumeToUpdate.probeCounts[0]; + uint distanceTextureWidth = probeGridWidth * ProbeFinalize::NUM_DISTANCE_TEXELS_PER_PROBE; + + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, distanceTextureWidth, probeGridHeight, 1); + } + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::BarrierDomain::Global, volumeBlendTextures); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Radiance Columns Fixup"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBorderRadianceColumnsFixup); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + uint probeGridWidth = volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2]; + uint probeGridHeight = volumeToUpdate.probeCounts[0]; + uint irradianceTextureHeight = probeGridHeight * ProbeFinalize::NUM_IRRADIANCE_TEXELS_PER_PROBE; + + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, probeGridWidth * 2, irradianceTextureHeight, 1); + } + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Distance Columns Fixup"); + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probeBorderDistanceColumnsFixup); + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + uint probeGridWidth = volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2]; + uint probeGridHeight = volumeToUpdate.probeCounts[0]; + uint distanceTextureHeight = probeGridHeight * ProbeFinalize::NUM_DISTANCE_TEXELS_PER_PROBE; + + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.blendProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, probeGridWidth * 2, distanceTextureHeight, 1); + } + CoreGraphics::CmdEndMarker(cmdBuf); + + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::BarrierDomain::Global, volumeBlendTextures); + + for (const UpdateVolume& volumeToUpdate : state.volumesToUpdate) + { + if (volumeToUpdate.relocateAndClassifyProbesTable != CoreGraphics::InvalidResourceTableId) + { + CoreGraphics::CmdBeginMarker(cmdBuf, NEBULA_MARKER_ORANGE, "DDGI Relocate and Classify Probes"); + + CoreGraphics::TextureBarrierInfo bar0 = + { + .tex = volumeToUpdate.volumeStateOutputs.offsetsTexture, + .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() + }; + CoreGraphics::TextureBarrierInfo bar1 = + { + .tex = volumeToUpdate.volumeStateOutputs.statesTexture, + .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() + }; + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::PipelineStage::ComputeShaderWrite, CoreGraphics::BarrierDomain::Global, { bar0, bar1 }); + + CoreGraphics::CmdSetShaderProgram(cmdBuf, state.probesRelocateAndClassifyProgram); + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToUpdate.relocateAndClassifyProbesTable, NEBULA_SYSTEM_GROUP, CoreGraphics::ComputePipeline, nullptr); + CoreGraphics::CmdDispatch(cmdBuf, Math::divandroundup(volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2], 8), Math::divandroundup(volumeToUpdate.probeCounts[0], 4), 1); + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::ComputeShaderWrite, CoreGraphics::PipelineStage::ComputeShaderRead, CoreGraphics::BarrierDomain::Global, { bar0, bar1 }); + + CoreGraphics::CmdEndMarker(cmdBuf); + } + } + } + }); + +#ifndef PUBLIC_BUILD + FrameScript_default::RegisterSubgraphPipelines_DDGIDebug_Pass([](const CoreGraphics::PassId pass, const uint subpass) + { + state.debugPipeline = CoreGraphics::CreateGraphicsPipeline( + { + .shader = state.debugProgram, + .pass = pass, + .subpass = subpass, + .inputAssembly = CoreGraphics::InputAssemblyKey{ { .topo = CoreGraphics::PrimitiveTopology::TriangleList, .primRestart = false } } + }); + }); + + FrameScript_default::RegisterSubgraph_DDGIDebug_Pass([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) + { + if (Core::CVarReadInt(g_debug_ddgi) == 1) + { + CoreGraphics::MeshBind(state.debugMesh, cmdBuf); + CoreGraphics::CmdSetGraphicsPipeline(cmdBuf, state.debugPipeline); + + for (const UpdateVolume& volumeToDraw : state.volumesToDraw) + { + CoreGraphics::CmdSetResourceTable(cmdBuf, volumeToDraw.debugTable, NEBULA_SYSTEM_GROUP, CoreGraphics::GraphicsPipeline, nullptr); + const CoreGraphics::PrimitiveGroup& prim = CoreGraphics::MeshGetPrimitiveGroup(state.debugMesh, 0); + CoreGraphics::CmdDraw(cmdBuf, volumeToDraw.probeCounts[0] * volumeToDraw.probeCounts[1] * volumeToDraw.probeCounts[2], prim); + } + } + }); +#endif + } //------------------------------------------------------------------------------ /** */ -DDGIContext::~DDGIContext() +Math::vec4 +SphericalFibonacci(float index, float numSamples) +{ + const float b = (sqrt(5.0f) * 0.5f + 0.5f) - 1.0f; + float phi = 2 * PI * Math::fract(index * b); + float cosTheta = 1.0f - (2.0f * index + 1.0f) * (1.0f / numSamples); + float sinTheta = sqrt(Math::clamp(1.0f - (cosTheta * cosTheta), 0.0f, 1.0f)); + return Math::vec4(Math::normalize(Math::vec3((cos(phi) * sinTheta), (sin(phi) * sinTheta), cosTheta)), 0); +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::Discard() { } //------------------------------------------------------------------------------ /** + * */ void -DDGIContext::SetupVolume(const Graphics::GraphicsEntityId id) +DDGIContext::SetupVolume(const Graphics::GraphicsEntityId id, const VolumeSetup& setup) { + if (!CoreGraphics::RayTracingSupported) + return; + + n_assert_msg(setup.numRaysPerProbe < 1024, "Maximum allowed number of rays per probe is 1024"); + + ContextEntityId ctxId = GetContextId(id); + Volume& volume = ddgiVolumeAllocator.Get<0>(ctxId.id); + volume.boundingBox.set(setup.position, setup.size); + volume.position = setup.position; + volume.size = setup.size; + volume.numProbesX = setup.numProbesX; + volume.numProbesY = setup.numProbesY; + volume.numProbesZ = setup.numProbesZ; + volume.numRaysPerProbe = Math::max(setup.numRaysPerProbe, ProbeUpdate::DDGI_NUM_FIXED_RAYS); + volume.options = setup.options; + + //volume.options.flags.lowPrecisionTextures = true; + + volume.normalBias = setup.normalBias; + volume.viewBias = setup.viewBias; + volume.irradianceScale = setup.irradianceScale; + volume.distanceExponent = setup.distanceExponent; + volume.encodingGamma = setup.encodingGamma; + volume.changeThreshold = setup.changeThreshold; + volume.brightnessThreshold = setup.brightnessThreshold; + volume.hysteresis = setup.hysteresis; + volume.blend = setup.blend; + volume.blendCutoff = setup.blendCutoff; + +#if NEBULA_GRAPHICS_DEBUG + Util::String volumeName = Util::String::Sprintf("DDGIVolume_%d", ctxId.id); +#endif + CoreGraphics::TextureCreateInfo radianceCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + radianceCreateInfo.name = Util::String::Sprintf("%s Radiance", volumeName.c_str()); +#endif + radianceCreateInfo.width = volume.numRaysPerProbe; + radianceCreateInfo.height = setup.numProbesX * setup.numProbesY * setup.numProbesZ; + radianceCreateInfo.format = CoreGraphics::PixelFormat::R32G32B32A32F; + radianceCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + + volume.radiance = CoreGraphics::CreateTexture(radianceCreateInfo); + + CoreGraphics::TextureCreateInfo irradianceCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + irradianceCreateInfo.name = Util::String::Sprintf("%s Irradiance", volumeName.c_str()); +#endif + irradianceCreateInfo.width = setup.numProbesY * setup.numProbesZ * (ProbeUpdate::NUM_IRRADIANCE_TEXELS_PER_PROBE + 2); + irradianceCreateInfo.height = setup.numProbesX * (ProbeUpdate::NUM_IRRADIANCE_TEXELS_PER_PROBE + 2); + irradianceCreateInfo.format = CoreGraphics::PixelFormat::R32G32B32A32F; + irradianceCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + volume.irradiance = CoreGraphics::CreateTexture(irradianceCreateInfo); + + CoreGraphics::TextureCreateInfo distanceCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + distanceCreateInfo.name = Util::String::Sprintf("%s Distance", volumeName.c_str()); +#endif + distanceCreateInfo.width = setup.numProbesY * setup.numProbesZ * (ProbeUpdate::NUM_DISTANCE_TEXELS_PER_PROBE + 2); + distanceCreateInfo.height = setup.numProbesX * (ProbeUpdate::NUM_DISTANCE_TEXELS_PER_PROBE + 2); + distanceCreateInfo.format = CoreGraphics::PixelFormat::R16G16F; + distanceCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + volume.distance = CoreGraphics::CreateTexture(distanceCreateInfo); + + CoreGraphics::TextureCreateInfo offsetCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + offsetCreateInfo.name = Util::String::Sprintf("%s Offsets", volumeName.c_str()); +#endif + offsetCreateInfo.width = setup.numProbesY * setup.numProbesZ; + offsetCreateInfo.height = setup.numProbesX; + offsetCreateInfo.format = CoreGraphics::PixelFormat::R16G16B16A16F; + offsetCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + volume.offsets = CoreGraphics::CreateTexture(offsetCreateInfo); + + CoreGraphics::TextureCreateInfo statesCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + statesCreateInfo.name = Util::String::Sprintf("%s States", volumeName.c_str()); +#endif + statesCreateInfo.width = setup.numProbesY * setup.numProbesZ; + statesCreateInfo.height = setup.numProbesX; + statesCreateInfo.format = CoreGraphics::PixelFormat::R8; + statesCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + volume.states = CoreGraphics::CreateTexture(statesCreateInfo); + + CoreGraphics::TextureCreateInfo scrollSpaceCreateInfo; +#if NEBULA_GRAPHICS_DEBUG + scrollSpaceCreateInfo.name = Util::String::Sprintf("%s ScrollSpace", volumeName.c_str()); +#endif + scrollSpaceCreateInfo.width = setup.numProbesY * setup.numProbesZ; + scrollSpaceCreateInfo.height = setup.numProbesX; + scrollSpaceCreateInfo.format = CoreGraphics::PixelFormat::R8; + scrollSpaceCreateInfo.usage = CoreGraphics::TextureUsage::ReadWriteTexture; + volume.scrollSpace = CoreGraphics::CreateTexture(scrollSpaceCreateInfo); + + for (uint rayIndex = 0; rayIndex < volume.numRaysPerProbe; rayIndex++) + { + SphericalFibonacci(rayIndex, volume.numRaysPerProbe).store(volume.volumeConstants.Directions[rayIndex]); + } + + // Store another set of minimal ray directions for probe activity updates + for (uint rayIndex = 0; rayIndex < ProbeUpdate::DDGI_NUM_FIXED_RAYS; rayIndex++) + { + SphericalFibonacci(rayIndex, ProbeUpdate::DDGI_NUM_FIXED_RAYS).store(volume.volumeConstants.MinimalDirections[rayIndex]); + } + + // Store another set of minimal ray directions for probe activity updates + for (uint rayIndex = 0; rayIndex < (volume.numRaysPerProbe - ProbeUpdate::DDGI_NUM_FIXED_RAYS); rayIndex++) + { + SphericalFibonacci(rayIndex, volume.numRaysPerProbe - ProbeUpdate::DDGI_NUM_FIXED_RAYS).store(volume.volumeConstants.ExtraDirections[rayIndex]); + } + + volume.volumeConstants.ProbeIrradiance = CoreGraphics::TextureGetBindlessHandle(volume.irradiance); + volume.volumeConstants.ProbeDistances = CoreGraphics::TextureGetBindlessHandle(volume.distance); + volume.volumeConstants.ProbeOffsets = CoreGraphics::TextureGetBindlessHandle(volume.offsets); + volume.volumeConstants.ProbeStates = CoreGraphics::TextureGetBindlessHandle(volume.states); + volume.volumeConstants.ProbeScrollSpace = CoreGraphics::TextureGetBindlessHandle(volume.scrollSpace); + volume.volumeConstants.ProbeRadiance = CoreGraphics::TextureGetBindlessHandle(volume.radiance); + + CoreGraphics::BufferCreateInfo probeVolumeUpdateBufferInfo; +#if NEBULA_GRAPHICS_DEBUG + probeVolumeUpdateBufferInfo.name = Util::String::Sprintf("%s Volume Update Constants", volumeName.c_str()); +#endif + probeVolumeUpdateBufferInfo.byteSize = sizeof(ProbeUpdate::VolumeConstants); + probeVolumeUpdateBufferInfo.usageFlags = CoreGraphics::BufferUsageFlag::ConstantBuffer; + probeVolumeUpdateBufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceAndHost; + volume.volumeConstantBuffer = CoreGraphics::CreateBuffer(probeVolumeUpdateBufferInfo); + + volume.updateProbesTable = CoreGraphics::ShaderCreateResourceTable(state.probeUpdateShader, NEBULA_SYSTEM_GROUP, 1); + CoreGraphics::ResourceTableSetRWTexture(volume.updateProbesTable, CoreGraphics::ResourceTableTexture(volume.radiance, ProbeUpdate::Table_System::RadianceOutput_SLOT)); + CoreGraphics::ResourceTableSetConstantBuffer(volume.updateProbesTable, CoreGraphics::ResourceTableBuffer(volume.volumeConstantBuffer, ProbeUpdate::Table_System::VolumeConstants_SLOT)); + CoreGraphics::ResourceTableCommitChanges(volume.updateProbesTable); + + volume.blendProbesTable = CoreGraphics::ShaderCreateResourceTable(state.probeFinalizeShader, NEBULA_SYSTEM_GROUP, 1); + CoreGraphics::ResourceTableSetConstantBuffer(volume.blendProbesTable, CoreGraphics::ResourceTableBuffer(volume.volumeConstantBuffer, ProbeFinalize::Table_System::VolumeConstants_SLOT)); + CoreGraphics::ResourceTableSetRWTexture(volume.blendProbesTable, CoreGraphics::ResourceTableTexture(volume.irradiance, ProbeFinalize::Table_System::IrradianceOutput_SLOT)); + CoreGraphics::ResourceTableSetRWTexture(volume.blendProbesTable, CoreGraphics::ResourceTableTexture(volume.distance, ProbeFinalize::Table_System::DistanceOutput_SLOT)); + CoreGraphics::ResourceTableSetRWTexture(volume.blendProbesTable, CoreGraphics::ResourceTableTexture(volume.scrollSpace, ProbeFinalize::Table_System::ScrollSpaceOutput_SLOT)); + CoreGraphics::ResourceTableCommitChanges(volume.blendProbesTable); + + volume.relocateProbesTable = CoreGraphics::ShaderCreateResourceTable(state.probesRelocateAndClassifyShader, NEBULA_SYSTEM_GROUP, 1); + CoreGraphics::ResourceTableSetConstantBuffer(volume.relocateProbesTable, CoreGraphics::ResourceTableBuffer(volume.volumeConstantBuffer, ProbeRelocateAndClassify::Table_System::VolumeConstants_SLOT)); + CoreGraphics::ResourceTableSetRWTexture(volume.relocateProbesTable, CoreGraphics::ResourceTableTexture(volume.offsets, ProbeRelocateAndClassify::Table_System::ProbeOffsetsOutput_SLOT)); + CoreGraphics::ResourceTableSetRWTexture(volume.relocateProbesTable, CoreGraphics::ResourceTableTexture(volume.states, ProbeRelocateAndClassify::Table_System::ProbeStateOutput_SLOT)); + CoreGraphics::ResourceTableCommitChanges(volume.relocateProbesTable); + + +#ifndef PUBLIC_BUILD + volume.debugResourceTable = CoreGraphics::ShaderCreateResourceTable(state.debugShader, NEBULA_SYSTEM_GROUP, 1); + CoreGraphics::ResourceTableSetConstantBuffer(volume.debugResourceTable, CoreGraphics::ResourceTableBuffer(volume.volumeConstantBuffer, ProbeDebug::Table_System::VolumeConstants_SLOT)); + CoreGraphics::ResourceTableCommitChanges(volume.debugResourceTable); +#endif +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::SetPosition(const Graphics::GraphicsEntityId id, const Math::vec3& position) +{ + ContextEntityId ctxId = GetContextId(id); + Volume& volume = ddgiVolumeAllocator.Get<0>(ctxId.id); + volume.position = position; + volume.boundingBox.set(volume.position, volume.size); +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::SetSize(const Graphics::GraphicsEntityId id, const Math::vec3& size) +{ + ContextEntityId ctxId = GetContextId(id); + Volume& volume = ddgiVolumeAllocator.Get<0>(ctxId.id); + volume.size = size; + volume.boundingBox.set(volume.position, volume.size); +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::UpdateActiveVolumes(const Ptr& view, const Graphics::FrameContext& ctx) +{ + if (!CoreGraphics::RayTracingSupported) + return; + + const Math::point cameraPos = CameraContext::GetTransform(view->GetCamera()).position; + const Util::Array& volumes = ddgiVolumeAllocator.GetArray<0>(); + Math::mat4 viewTransform = Graphics::CameraContext::GetView(view->GetCamera()); + const auto& projectSettings = Options::ProjectSettings; + float budget = Math::clamp(projectSettings.gi_settings->update_budget, 0.01f, 1.0f); + + state.volumesToUpdate.Clear(); + state.volumesToDraw.Clear(); + + state.elapsedTime += ctx.frameTime; + bool updateThisFrame = state.elapsedTime >= projectSettings.gi_settings->update_frequency; + if (updateThisFrame) + state.elapsedTime = 0; + + uint volumeCount = 0; + for (Volume& activeVolume : volumes) + { + UpdateVolume volumeToUpdate; + + volumeToUpdate.probeCounts[0] = activeVolume.numProbesX; + volumeToUpdate.probeCounts[1] = activeVolume.numProbesY; + volumeToUpdate.probeCounts[2] = activeVolume.numProbesZ; + volumeToUpdate.numRays = activeVolume.numRaysPerProbe; + volumeToUpdate.probeUpdateOutputs.radianceDistanceTexture = activeVolume.radiance; + volumeToUpdate.volumeBlendOutputs.irradianceTexture = activeVolume.irradiance; + volumeToUpdate.volumeBlendOutputs.distanceTexture = activeVolume.distance; + volumeToUpdate.volumeBlendOutputs.scrollSpaceTexture = activeVolume.scrollSpace; + volumeToUpdate.volumeStateOutputs.offsetsTexture = activeVolume.offsets; + volumeToUpdate.volumeStateOutputs.statesTexture = activeVolume.states; + volumeToUpdate.updateProbesTable = CoreGraphics::InvalidResourceTableId; + volumeToUpdate.blendProbesTable = CoreGraphics::InvalidResourceTableId; + volumeToUpdate.relocateAndClassifyProbesTable = CoreGraphics::InvalidResourceTableId; + + float u1 = 2 * PI * Math::rand(); + float cos1 = Math::cos(u1); + float sin1 = Math::sin(u1); + + float u2 = 2 * PI * Math::rand(); + float cos2 = Math::cos(u2); + float sin2 = Math::sin(u2); + + float u3 = Math::rand(); + float sq3 = 2 * Math::sqrt(u3 * (1 - u3)); + float s2 = 2 * u3 * sin2 * sin2 - 1.0f; + float c2 = 2 * u3 * cos2 * cos2 - 1.0f; + float sc = 2 * u3 * sin2 * cos2; + + Math::mat4 randomRotation = Math::mat4( + Math::vec4(cos1 * c2 - sin1 * sc, sin1 * sc + cos1 * s2, sq3 * cos2, 0), + Math::vec4(cos1 * sc - sin1 * s2, sin1 * sc + cos1 * s2, sq3 * sin2, 0), + Math::vec4(cos1 * (sq3 * cos2) - sin1 * (sq3 * sin2), sin1 * (sq3 * cos2) + cos1 * (sq3 * sin2), 1 - 2 * u3, 0), + Math::vec4(0, 0, 0, 1) + ); + + + Math::vec3 size = activeVolume.size; + randomRotation.store(activeVolume.volumeConstants.TemporalRotation); + size.store(activeVolume.volumeConstants.Scale); + activeVolume.position.store(activeVolume.volumeConstants.Offset); + activeVolume.volumeConstants.NumIrradianceTexels = ProbeUpdate::NUM_IRRADIANCE_TEXELS_PER_PROBE; + activeVolume.volumeConstants.ProbeGridDimensions[0] = activeVolume.numProbesX; + activeVolume.volumeConstants.ProbeGridDimensions[1] = activeVolume.numProbesY; + activeVolume.volumeConstants.ProbeGridDimensions[2] = activeVolume.numProbesZ; + activeVolume.volumeConstants.Options = 0; + activeVolume.volumeConstants.Options |= activeVolume.options.flags.relocate ? ProbeUpdate::RELOCATION_OPTION : 0x0; + activeVolume.volumeConstants.Options |= activeVolume.options.flags.scrolling ? ProbeUpdate::SCROLL_OPTION : 0x0; + activeVolume.volumeConstants.Options |= activeVolume.options.flags.classify ? ProbeUpdate::CLASSIFICATION_OPTION : 0x0; + activeVolume.volumeConstants.Options |= activeVolume.options.flags.lowPrecisionTextures ? ProbeUpdate::LOW_PRECISION_IRRADIANCE_OPTION : 0x0; + activeVolume.volumeConstants.Options |= budget != 1.0f ? ProbeUpdate::PARTIAL_UPDATE_OPTION : 0x0; + uint numProbes = volumeToUpdate.probeCounts[0] * volumeToUpdate.probeCounts[1] * volumeToUpdate.probeCounts[2]; + activeVolume.volumeConstants.ProbeIndexStart = (activeVolume.volumeConstants.ProbeIndexStart + activeVolume.volumeConstants.ProbeIndexCount) % numProbes; + activeVolume.volumeConstants.ProbeIndexCount = Math::min(uint(numProbes * budget), numProbes - activeVolume.volumeConstants.ProbeIndexStart); + activeVolume.volumeConstants.ProbeScrollOffsets[0] = 0; + activeVolume.volumeConstants.ProbeScrollOffsets[1] = 0; + activeVolume.volumeConstants.ProbeScrollOffsets[2] = 0; + Math::quat().store(activeVolume.volumeConstants.Rotation); + activeVolume.volumeConstants.ProbeGridSpacing[0] = size[0] * 2.0f / float(activeVolume.numProbesX); + activeVolume.volumeConstants.ProbeGridSpacing[1] = size[1] * 2.0f / float(activeVolume.numProbesY); + activeVolume.volumeConstants.ProbeGridSpacing[2] = size[2] * 2.0f / float(activeVolume.numProbesZ); + activeVolume.volumeConstants.NumDistanceTexels = ProbeUpdate::NUM_DISTANCE_TEXELS_PER_PROBE; + activeVolume.volumeConstants.IrradianceGamma = activeVolume.encodingGamma; + activeVolume.volumeConstants.NormalBias = activeVolume.normalBias; + activeVolume.volumeConstants.ViewBias = activeVolume.viewBias; + activeVolume.volumeConstants.IrradianceScale = activeVolume.irradianceScale; + + activeVolume.volumeConstants.BackfaceThreshold = activeVolume.backfaceThreshold; + activeVolume.volumeConstants.MinFrontfaceDistance = activeVolume.minFrontfaceDistance; + activeVolume.volumeConstants.ProbeDistanceScale = 1.0f; + + activeVolume.volumeConstants.RaysPerProbe = activeVolume.numRaysPerProbe; + activeVolume.volumeConstants.InverseGammaEncoding = 1.0f / activeVolume.volumeConstants.IrradianceGamma; + activeVolume.volumeConstants.Hysteresis = activeVolume.hysteresis; + activeVolume.volumeConstants.NormalBias = activeVolume.normalBias; + activeVolume.volumeConstants.ViewBias = activeVolume.viewBias; + + activeVolume.volumeConstants.IrradianceScale = activeVolume.irradianceScale; + activeVolume.volumeConstants.DistanceExponent = activeVolume.distanceExponent; + activeVolume.volumeConstants.ChangeThreshold = activeVolume.changeThreshold; + activeVolume.volumeConstants.BrightnessThreshold = activeVolume.brightnessThreshold; + + activeVolume.volumeConstants.DebugSize = Core::CVarReadFloat(g_debug_ddgi_probe_size); + + CoreGraphics::BufferUpdate(activeVolume.volumeConstantBuffer, activeVolume.volumeConstants); + + + // Only update the tables if the volume is within update range + if (activeVolume.boundingBox.contains(xyz(cameraPos))) + { + volumeToUpdate.updateProbesTable = activeVolume.updateProbesTable; + volumeToUpdate.blendProbesTable = activeVolume.blendProbesTable; + if (activeVolume.options.flags.relocate || activeVolume.options.flags.classify) + volumeToUpdate.relocateAndClassifyProbesTable = activeVolume.relocateProbesTable; + if (updateThisFrame) + { + state.volumesToUpdate.Append(volumeToUpdate); + } + } + +#ifndef PUBLIC_BUILD + volumeToUpdate.debugTable = activeVolume.debugResourceTable; + state.volumesToDraw.Append(volumeToUpdate); +#endif + + auto& giVolume = state.giVolumes[volumeCount]; + + Math::mat4 transform = Math::scaling(activeVolume.size * 2.0f); + transform.translate(activeVolume.position); + Math::bbox bbox = viewTransform * transform; + bbox.pmin.store(giVolume.bboxMin); + bbox.pmax.store(giVolume.bboxMax); + Math::quat().store(giVolume.Rotation); + activeVolume.position.store(giVolume.Offset); + giVolume.NumDistanceTexels = ProbeUpdate::NUM_DISTANCE_TEXELS_PER_PROBE; + giVolume.NumIrradianceTexels = ProbeUpdate::NUM_IRRADIANCE_TEXELS_PER_PROBE; + giVolume.EncodingGamma = activeVolume.volumeConstants.IrradianceGamma; + giVolume.IrradianceScale = activeVolume.volumeConstants.IrradianceScale; + memcpy(giVolume.GridCounts, activeVolume.volumeConstants.ProbeGridDimensions, sizeof(activeVolume.volumeConstants.ProbeGridDimensions)); + memcpy(giVolume.ScrollOffsets, activeVolume.volumeConstants.ProbeScrollOffsets, sizeof(activeVolume.volumeConstants.ProbeScrollOffsets)); + memcpy(giVolume.GridSpacing, activeVolume.volumeConstants.ProbeGridSpacing, sizeof(activeVolume.volumeConstants.ProbeGridSpacing)); + activeVolume.size.store(giVolume.Size); + + giVolume.BlendCutoff = activeVolume.blendCutoff; + giVolume.Blend = activeVolume.blend; + giVolume.ViewBias = activeVolume.viewBias; + giVolume.NormalBias = activeVolume.normalBias; + giVolume.Options = activeVolume.volumeConstants.Options; + giVolume.Distances = activeVolume.volumeConstants.ProbeDistances; + giVolume.Irradiance = activeVolume.volumeConstants.ProbeIrradiance; + giVolume.States = activeVolume.volumeConstants.ProbeStates; + giVolume.Offsets = activeVolume.volumeConstants.ProbeOffsets; + + volumeCount++; + } + + // Update shared GI data + Shared::GIVolumeUniforms giVolumeUniforms; + giVolumeUniforms.NumGIVolumes = volumeCount; + giVolumeUniforms.NumGIVolumeClusters = Clustering::ClusterContext::GetNumClusters(); + + IndexT bufferIndex = CoreGraphics::GetBufferedFrameIndex(); + + CoreGraphics::ResourceTableId frameResourceTable = Graphics::GetFrameResourceTable(bufferIndex); + + uint64 offset = CoreGraphics::SetConstants(giVolumeUniforms); + ResourceTableSetConstantBuffer(frameResourceTable, { CoreGraphics::GetConstantBuffer(bufferIndex), Shared::Table_Frame::GIVolumeUniforms_SLOT, 0, sizeof(Shared::GIVolumeUniforms), offset }); + ResourceTableCommitChanges(frameResourceTable); + + if (volumeCount > 0) + { + GiVolumeCull::GIVolumeLists volumeList; + Memory::CopyElements(state.giVolumes, volumeList.GIVolumes, volumeCount); + CoreGraphics::BufferUpdate(state.stagingClusterGIVolumeList.buffers[bufferIndex], volumeList); + CoreGraphics::BufferFlush(state.stagingClusterGIVolumeList.buffers[bufferIndex]); + } + + CoreGraphics::ResourceTableSetRWBuffer(state.raytracingTable.tables[ctx.bufferIndex], CoreGraphics::ResourceTableBuffer(Raytracing::RaytracingContext::GetObjectBindingBuffer(), ProbeUpdate::Table_Batch::ObjectBuffer_SLOT)); + CoreGraphics::ResourceTableSetAccelerationStructure(state.raytracingTable.tables[ctx.bufferIndex], CoreGraphics::ResourceTableTlas(Raytracing::RaytracingContext::GetTLAS(), ProbeUpdate::Table_Batch::TLAS_SLOT)); + CoreGraphics::ResourceTableCommitChanges(state.raytracingTable.tables[ctx.bufferIndex]); +} + +#ifndef PUBLIC_BUILD +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::OnRenderDebug(uint32_t flags) +{ + if (!CoreGraphics::RayTracingSupported) + return; + + CoreGraphics::ShapeRenderer* shapeRenderer = CoreGraphics::ShapeRenderer::Instance(); + + const Util::Array& volumes = ddgiVolumeAllocator.GetArray<0>(); + for (const Volume& volume : volumes) + { + CoreGraphics::RenderShape debugShape; + Math::mat4 transform; + transform.scale(volume.size); + transform.translate(volume.position); + + debugShape.SetupSimpleShape(CoreGraphics::RenderShape::Box, CoreGraphics::RenderShape::RenderFlag::CheckDepth, Math::vec4(0, 1, 0, 1), transform); + shapeRenderer->AddShape(debugShape); + } +} +#endif + +//------------------------------------------------------------------------------ +/** +*/ +Graphics::ContextEntityId +DDGIContext::Alloc() +{ + return ddgiVolumeAllocator.Alloc(); +} + +//------------------------------------------------------------------------------ +/** +*/ +void +DDGIContext::Dealloc(Graphics::ContextEntityId id) +{ + Volume& volume = ddgiVolumeAllocator.Get<0>(id.id); + CoreGraphics::DestroyTexture(volume.radiance); + CoreGraphics::DestroyTexture(volume.irradiance); + CoreGraphics::DestroyTexture(volume.distance); + CoreGraphics::DestroyTexture(volume.offsets); + CoreGraphics::DestroyTexture(volume.states); + CoreGraphics::DestroyTexture(volume.scrollSpace); + CoreGraphics::DestroyBuffer(volume.volumeConstantBuffer); + CoreGraphics::DestroyResourceTable(volume.updateProbesTable); + CoreGraphics::DestroyResourceTable(volume.blendProbesTable); + ddgiVolumeAllocator.Dealloc(id.id); } } // namespace GI diff --git a/code/render/gi/ddgicontext.h b/code/render/gi/ddgicontext.h index 6af0514dd..f676e5885 100644 --- a/code/render/gi/ddgicontext.h +++ b/code/render/gi/ddgicontext.h @@ -8,10 +8,25 @@ (C) 2024 Individual contributors, see AUTHORS file */ //------------------------------------------------------------------------------ +#include +#include + #include "graphics/graphicscontext.h" namespace GI { +union DDGIOptions +{ + struct + { + uint scrolling : 1; // Infinitely scrolls based on camera position + uint classify : 1; // Enables/disables probes based on hits + uint relocate : 1; // Relocate probes to avoid them being stuck inside geometry + uint lowPrecisionTextures : 1; // Use more compact texture formats at the expense of quality + } flags; + uint32 bits = 0x0; +}; + class DDGIContext : public Graphics::GraphicsContext { __DeclareContext() @@ -21,28 +36,93 @@ class DDGIContext : public Graphics::GraphicsContext /// Destructor ~DDGIContext(); + /// setup light context + static void Create(); + /// discard light context + static void Discard(); + + struct VolumeSetup + { + uint numProbesX, numProbesY, numProbesZ; + uint numRaysPerProbe; + Math::vec3 size; + Math::vec3 position; + + float normalBias = 0.1f; + float viewBias = 0.4f; + float irradianceScale = 1.0f; + float distanceExponent = 0.5f; + float encodingGamma = 5.0f; + float changeThreshold = 0.2f; + float brightnessThreshold = 2.0f; + float hysteresis = 0.97f; + float blendCutoff = 0.0f; + float blend = 0.0f; + float updateBudget = 1.0f; + DDGIOptions options; + }; /// Create volume - void SetupVolume(const Graphics::GraphicsEntityId id); + static void SetupVolume(const Graphics::GraphicsEntityId id, const VolumeSetup& setup); + /// Set volume position + static void SetPosition(const Graphics::GraphicsEntityId id, const Math::vec3& position); + /// Set volume scale + static void SetSize(const Graphics::GraphicsEntityId id, const Math::vec3& size); + + /// prepare light lists + static void UpdateActiveVolumes(const Ptr& view, const Graphics::FrameContext& ctx); + +#ifndef PUBLIC_BUILD + static void OnRenderDebug(uint32_t flags); +#endif private: struct Volume { - CoreGraphics::TextureId radiance, normals, depth; - CoreGraphics::BufferId constants, probeBuffer; + uint numProbesX, numProbesY, numProbesZ; + uint numRaysPerProbe; + Math::vec3 size; + Math::vec3 position; + Math::bbox boundingBox; + CoreGraphics::TextureId radiance; // Ray tracing output + CoreGraphics::TextureId irradiance, distance, offsets, states, scrollSpace; + CoreGraphics::BufferId volumeConstantBuffer; + CoreGraphics::ResourceTableId updateProbesTable, blendProbesTable, relocateProbesTable; + + float normalBias = 0.1f; + float viewBias = 0.4f; + float irradianceScale = 1.0f; + float distanceExponent = 50.0f; + float encodingGamma = 5.0f; + float changeThreshold = 0.2f; + float brightnessThreshold = 2.0f; + float backfaceThreshold = 0.25f; + float minFrontfaceDistance = 0.1f; + float hysteresis = 0.97f; + float blendCutoff = 0.0f; + float blend = 0.0f; + + ProbeUpdate::VolumeConstants volumeConstants; + DDGIOptions options; + +#ifndef PUBLIC_BUILD + CoreGraphics::ResourceTableId debugResourceTable; +#endif }; + typedef Ids::IdAllocator< Volume > DDGIVolumeAllocator; - static DDGIVolumeAllocator allocator; + static DDGIVolumeAllocator ddgiVolumeAllocator; + - typedef Ids::IdAllocator< - Graphics::GraphicsEntityId - > ContributorAllocator; - static ContributorAllocator contributorAllocator; + /// allocate a new slice for this context + static Graphics::ContextEntityId Alloc(); + /// deallocate a slice + static void Dealloc(Graphics::ContextEntityId id); }; } // namespace GI diff --git a/code/render/gi/shaders/ddgi.fxh b/code/render/gi/shaders/ddgi.fxh deleted file mode 100644 index eacaf95b5..000000000 --- a/code/render/gi/shaders/ddgi.fxh +++ /dev/null @@ -1,5 +0,0 @@ -//------------------------------------------------------------------------------ -// @file ddgi.fxh -// @copyright (C) 2024 Individual contributors, see AUTHORS file -//------------------------------------------------------------------------------ - diff --git a/code/render/gi/shaders/gi_volume_cull.fx b/code/render/gi/shaders/gi_volume_cull.fx new file mode 100644 index 000000000..f08446d48 --- /dev/null +++ b/code/render/gi/shaders/gi_volume_cull.fx @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// givolumecull.fx +// (C) 2019 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ +#include "lib/std.fxh" +#include "lib/util.fxh" +#include "lib/shared.fxh" +#include "lib/clustering.fxh" + +write rgba16f image2D DebugOutput; +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 64 +shader +void csCull() +{ + uint index1D = gl_GlobalInvocationID.x; + + if (index1D > NumGIVolumeClusters) + return; + + ClusterAABB aabb = AABBs[index1D]; + + uint flags = 0; + + // update PBR decals + uint numVolumes = 0; + for (uint i = 0; i < NumGIVolumes; i++) + { + const GIVolume volume = GIVolumes[i]; + if (TestAABBAABB(aabb, volume.bboxMin.xyz, volume.bboxMax.xyz)) + { + GIVolumeIndexLists[index1D * MAX_GI_VOLUMES_PER_CLUSTER + numVolumes] = i; + numVolumes++; + } + } + GIVolumeCountList[index1D] = numVolumes; + + // update feature flags if we have any decals + if (numVolumes > 0) + flags |= CLUSTER_GI_VOLUME_BIT; + + atomicOr(AABBs[index1D].featureFlags, flags); +} + + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 64 +shader +void csDebug() +{ + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + float depth = fetch2D(DepthBuffer, PointSampler, coord, 0).r; + + // convert screen coord to view-space position + vec4 worldPos = PixelToWorld(coord * InvFramebufferDimensions, depth, InvView, InvProjection); + + float viewDepth = CalculateViewDepth(View, worldPos.xyz); + + uint3 index3D = CalculateClusterIndex(coord / BlockSize, viewDepth, InvZScale, InvZBias); + uint idx = Pack3DTo1D(index3D, NumCells.x, NumCells.y); + + uint flag = AABBs[idx].featureFlags; // add 0 so we can read the value + vec4 color = vec4(0, 0, 0, 0); + if (CHECK_FLAG(flag, CLUSTER_GI_VOLUME_BIT)) + { + uint count = GIVolumeCountList[idx]; + color.r = count / float(NumGIVolumes); + } + + imageStore(DebugOutput, int2(coord), color); +} + +//------------------------------------------------------------------------------ +/** +*/ +program CullGIVolumes [ string Mask = "Cull"; ] +{ + ComputeShader = csCull(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program ClusterDebug [ string Mask = "Debug"; ] +{ + ComputeShader = csDebug(); +}; diff --git a/code/render/gi/shaders/probe_debug.fx b/code/render/gi/shaders/probe_debug.fx new file mode 100644 index 000000000..a295daada --- /dev/null +++ b/code/render/gi/shaders/probe_debug.fx @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// @file probe_debug.fx +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "probe_shared.fxh" + + +//------------------------------------------------------------------------------ +/** +*/ +shader void +DebugVS( + [slot=0] in vec3 position, + [slot=2] in ivec2 uv, + [slot=1] in vec3 normal, + [slot=3] in vec4 tangent, + out vec3 Normal, + out vec3 WorldPosition, + out flat int Instance +) +{ + vec3 probeWorldPosition; + if ((Options & RELOCATION_OPTION) != 0) + probeWorldPosition = DDGIProbeWorldPositionWithOffset(gl_InstanceIndex, Offset, Rotation, ProbeGridDimensions, ProbeGridSpacing, ProbeOffsets); + else + probeWorldPosition = DDGIProbeWorldPosition(gl_InstanceIndex, Offset, Rotation, ProbeGridDimensions, ProbeGridSpacing); + WorldPosition = probeWorldPosition; + + Normal = normal; + Instance = gl_InstanceIndex; + gl_Position = ViewProjection * vec4(position * DebugSize + probeWorldPosition, 1); +} + +//------------------------------------------------------------------------------ +/** +*/ +shader void +DebugPS( + in vec3 normal + , in vec3 worldPos + , in flat int instance + , [color0] out vec4 Color +) +{ + GIVolume volumeArg; + volumeArg.Offset = Offset; + volumeArg.Rotation = Rotation; + volumeArg.GridCounts = ProbeGridDimensions; + volumeArg.GridSpacing = ProbeGridSpacing; + volumeArg.ScrollOffsets = ProbeScrollOffsets; + volumeArg.NumIrradianceTexels = NumIrradianceTexels; + volumeArg.NumDistanceTexels = NumDistanceTexels; + volumeArg.EncodingGamma = IrradianceGamma; + volumeArg.Irradiance = ProbeIrradiance; + volumeArg.Distances = ProbeDistances; + volumeArg.Offsets = ProbeOffsets; + volumeArg.States = ProbeStates; + + if ((Options & CLASSIFICATION_OPTION) != 0) + { + ivec2 probeTexel = DDGIProbeTexelPosition(instance, ProbeGridDimensions); + float status = fetch2D(ProbeStates, Basic2DSampler, probeTexel, 0).r; + if (status == PROBE_STATE_INACTIVE) + { + Color = vec4(mix(vec3(1,0,0), vec3(0,0,1), vec3(hash12(gl_FragCoord.xy))), 1); + return; + } + } + + vec3 irradiance = EvaluateDDGIIrradiance(worldPos, vec3(0), normal, volumeArg, Options); + Color = vec4(irradiance, 1); +} + +render_state DefaultState +{ + DepthWrite = true; + DepthEnabled = true; + DepthFunc = LessEqual; +}; + +//------------------------------------------------------------------------------ +/** +*/ +program Debug[string Mask="Debug";] +{ + VertexShader = DebugVS(); + PixelShader = DebugPS(); + RenderState = DefaultState; +}; diff --git a/code/render/gi/shaders/probe_finalize.fx b/code/render/gi/shaders/probe_finalize.fx new file mode 100644 index 000000000..efba866a9 --- /dev/null +++ b/code/render/gi/shaders/probe_finalize.fx @@ -0,0 +1,544 @@ +//------------------------------------------------------------------------------ +// @file probe_finalize.fx +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ +#include +#include +#include + +group(SYSTEM_GROUP) read_write r11g11b10f image2D IrradianceOutput; +group(SYSTEM_GROUP) read_write rg16f image2D DistanceOutput; +group(SYSTEM_GROUP) read_write r8 image2D ScrollSpaceOutput; + +#include "probe_shared.fxh" + +groupshared vec3 Radiance[1024]; +groupshared float Distance[1024]; +groupshared vec3 Direction[1024]; + +const uint RADIANCE_MODE = 0; +const uint DISTANCE_MODE = 1; + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +UnpackUIntToFloat3(uint val) +{ + vec3 unpacked; + unpacked.x = float((val & 0x000003FF)) / 1023.0f; + unpacked.y = float(((val >> 10) & 0x000003FF)) / 1023.0f; + unpacked.z = float(((val >> 20) & 0x000003FF)) / 1023.0f; + return unpacked; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +RadianceHysteresis(vec3 previous, vec3 current, float hysteresis) +{ + if (DDGIMaxComponent(previous - current) > ChangeThreshold) + { + hysteresis = max(0.0f, hysteresis - 0.75f); + } + + vec3 delta = (current - previous); + if (length(delta) > BrightnessThreshold) + { + current = previous + (delta * 0.25f); + } + const vec3 ConstThreshold = vec3(1.0f / 1024.0f); + vec3 lerpDelta = (1.0f - hysteresis) * delta; + if (DDGIMaxComponent(current) < DDGIMaxComponent(previous)) + { + lerpDelta = min(max(ConstThreshold, abs(lerpDelta)), abs(delta)) * sign(lerpDelta); + } + return previous + lerpDelta; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec4 +LoadRadiance(ivec2 coords, uint mode) +{ +#ifdef USE_COMPRESSED_RADIANCE + vec2 value = fetch2D(ProbeRadiance, Basic2DSampler, coords, 0).xy; + vec4 res = vec4(0); + if (mode == RADIANCE_MODE) + res.xyz = UnpackUIntToFloat3(floatBitsToUint(value.x)); + res.w = value.y; + return res; +#else + vec4 value = fetch2D(ProbeRadiance, Basic2DSampler, coords, 0); + return value; +#endif +} + +//------------------------------------------------------------------------------ +/** +*/ +void +Blend(const uint MODE) +{ + bool earlyOut = false; + const int NUM_TEXELS_PER_PROBE = int(MODE == RADIANCE_MODE ? NUM_IRRADIANCE_TEXELS_PER_PROBE : NUM_DISTANCE_TEXELS_PER_PROBE); + vec4 result = vec4(0); + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + int probeIndex = DDGIProbeIndex(texel, ProbeGridDimensions, NUM_TEXELS_PER_PROBE); + int groupIndex = int(gl_LocalInvocationIndex); + if (probeIndex < 0) + { + earlyOut = true; + } + + uvec3 prevProbeSpace; + uvec3 probeSpace = uvec3(0); + ivec2 texelPosition; + if ((Options & SCROLL_OPTION) != 0) + { + int storageProbeIndex = DDGIProbeIndexOffset(probeIndex, ProbeGridDimensions, ProbeScrollOffsets); + texelPosition = DDGIProbeTexelPosition(storageProbeIndex, ProbeGridDimensions); + + prevProbeSpace.x = floatBitsToUint(imageLoad(ScrollSpaceOutput, texelPosition).x) & 0x01; + prevProbeSpace.y = (floatBitsToUint(imageLoad(ScrollSpaceOutput, texelPosition).x) & 0x02) >> 1; + prevProbeSpace.z = (floatBitsToUint(imageLoad(ScrollSpaceOutput, texelPosition).x) & 0x01) >> 2; + + ivec3 probeGridCoord = DDGIProbeCoords(probeIndex, ProbeGridDimensions); + probeSpace = (probeGridCoord + ProbeScrollOffsets) / ProbeGridDimensions; + probeSpace = probeSpace % 2; + + if ((Options & PARTIAL_UPDATE_OPTION) != 0) + { + if (prevProbeSpace.x == probeSpace.x && prevProbeSpace.y == probeSpace.y && prevProbeSpace.z == probeSpace.z) + earlyOut = true; + } + } + + if ((Options & PARTIAL_UPDATE_OPTION) != 0) + { + if (!earlyOut) + { + int numProbes = ProbeGridDimensions.x * ProbeGridDimensions.y * ProbeGridDimensions.z; + int probeRRIndex = (probeIndex < ProbeIndexStart) ? probeIndex + numProbes : probeIndex; + if (probeRRIndex >= ProbeIndexStart + ProbeIndexCount) + earlyOut = true; + } + } + + uvec2 probeTexCoords = uvec2(0); + int storageProbeIndex; + if (!earlyOut) + { + if ((Options & SCROLL_OPTION) != 0) + { + storageProbeIndex = DDGIProbeIndexOffset(probeIndex, ProbeGridDimensions, ProbeScrollOffsets); + uvec2 intraProbeTexelOffset = gl_GlobalInvocationID.xy % uvec2(NUM_TEXELS_PER_PROBE, NUM_TEXELS_PER_PROBE); + probeTexCoords = DDGIThreadBaseCoord(storageProbeIndex, ProbeGridDimensions, NUM_TEXELS_PER_PROBE) + intraProbeTexelOffset; + probeTexCoords.xy = probeTexCoords.xy + uvec2(1) + (probeTexCoords.xy / NUM_TEXELS_PER_PROBE) * 2; + } + else + { + storageProbeIndex = probeIndex; + probeTexCoords = gl_GlobalInvocationID.xy + uvec2(1); + probeTexCoords.xy += (gl_GlobalInvocationID.xy / NUM_TEXELS_PER_PROBE) * 2; + } + + if ((Options & CLASSIFICATION_OPTION) != 0) + { + texelPosition = DDGIProbeTexelPosition(storageProbeIndex, ProbeGridDimensions); + int probeState = floatBitsToInt(fetch2D(ProbeStates, Basic2DSampler, texelPosition, 0).x); + if (probeState == PROBE_STATE_INACTIVE) + { + earlyOut = true; + } + } + } + + vec2 probeOctantUV = vec2(0); + probeOctantUV = DDGINormalizedOctahedralCoordinates(texel, NUM_TEXELS_PER_PROBE); + vec3 probeRayDirection = DDGIOctahedralDirection(probeOctantUV); + + if (!earlyOut) + { + int totalIterations = int(ceil(float(RaysPerProbe) / float(NUM_TEXELS_PER_PROBE * NUM_TEXELS_PER_PROBE))); + for (int iteration = 0; iteration < totalIterations; iteration++) + { + int rayIndex = groupIndex * totalIterations + iteration; + if (rayIndex >= RaysPerProbe) + break; + + vec4 value = LoadRadiance(ivec2(rayIndex, probeIndex), MODE); + if (MODE == RADIANCE_MODE) + Radiance[rayIndex] = value.xyz; + Distance[rayIndex] = value.w; + Direction[rayIndex] = DDGIGetProbeDirection(rayIndex, TemporalRotation, Options); + } + } + + groupMemoryBarrier(); + barrier(); + + if (earlyOut) + { + return; + } + + int rayIndex = 0; + if ((Options & (CLASSIFICATION_OPTION | RELOCATION_OPTION)) != 0) + { + rayIndex = int(DDGI_NUM_FIXED_RAYS); + } + + uint backfaces; + uint maxBackfaces; + if (MODE == RADIANCE_MODE) + { + backfaces = 0; + maxBackfaces = uint((RaysPerProbe - rayIndex) * 0.1f); + } + + + for (; rayIndex < RaysPerProbe; rayIndex++) + { + vec3 rayDirection = Direction[rayIndex]; + float weight = max(0.0f, dot(probeRayDirection, rayDirection)); + ivec2 probeRayIndex = ivec2(rayIndex, probeIndex); + + if (MODE == RADIANCE_MODE) + { + vec3 probeRayRadiance = Radiance[rayIndex]; + float probeRayDistance = Distance[rayIndex]; + + if (probeRayDistance < 0.0f) + { + backfaces++; + if (backfaces >= maxBackfaces) + return; + continue; + } + + result += vec4(probeRayRadiance * weight, weight); + } + else + { + float probeMaxRayDistance = length(ProbeGridSpacing) * 1.5f; + weight = pow(weight, DistanceExponent); + + float probeRayDistance = min(abs(Distance[rayIndex]), probeMaxRayDistance); + + result += vec4(probeRayDistance * weight, (probeRayDistance * probeRayDistance), 0.0f, weight); + } + } + + const float epsilon = 1e-9f * float(RaysPerProbe); + result.rgb *= 1.0f / max(2.0f * result.w, epsilon); + + + if (MODE == DISTANCE_MODE) + { + uint probeSpacePacked = 0; + probeSpacePacked = probeSpace.x; + probeSpacePacked = probeSpace.y << 1; + probeSpacePacked = probeSpace.z << 2; + imageStore(ScrollSpaceOutput, texelPosition, vec4(uintBitsToFloat(probeSpacePacked), 0, 0, 0)); + } + + vec3 previous; + if (MODE == RADIANCE_MODE) + previous = imageLoad(IrradianceOutput, ivec2(probeTexCoords)).rgb; + else + previous = imageLoad(DistanceOutput, ivec2(probeTexCoords)).rgb; + float hysteresis = Hysteresis; + + if (MODE == RADIANCE_MODE) + { + result.xyz = pow(result.xyz, vec3(InverseGammaEncoding)); + + if ((Options & SCROLL_OPTION) != 0) + { + if (probeSpace.x == prevProbeSpace.x && probeSpace.y == prevProbeSpace.y && probeSpace.z == prevProbeSpace.z) + { + result = vec4(RadianceHysteresis(previous, result.rgb, hysteresis), 1.0f); + } + } + else + { + result = vec4(RadianceHysteresis(previous, result.rgb, hysteresis), 1.0f); + } + } + else + { + if ((Options & SCROLL_OPTION) != 0) + { + if (probeSpace.x == prevProbeSpace.x && probeSpace.y == prevProbeSpace.y && probeSpace.z == prevProbeSpace.z) + { + result = vec4(mix(result.xyz, previous.xyz, hysteresis), 1.0f); + } + } + else + { + result = vec4(mix(result.xyz, previous.xyz, hysteresis), 1.0f); + } + } + + if (MODE == RADIANCE_MODE) + imageStore(IrradianceOutput, ivec2(probeTexCoords), result); + else + imageStore(DistanceOutput, ivec2(probeTexCoords), result); +} + + +//------------------------------------------------------------------------------ +/** +*/ +void +BorderRows(const uint MODE) +{ + const int NUM_TEXELS_PER_PROBE = int(MODE == RADIANCE_MODE ? NUM_IRRADIANCE_TEXELS_PER_PROBE : NUM_DISTANCE_TEXELS_PER_PROBE); + + uint probeSideLength = (NUM_TEXELS_PER_PROBE + 2); + uint probeSideLengthMinusOne = (probeSideLength - 1); + + uvec2 thread = gl_GlobalInvocationID.xy; + thread.y *= probeSideLength; + + int mod = int(gl_GlobalInvocationID.x % probeSideLength); + if (mod == 0 || mod == probeSideLengthMinusOne) + return; + + uint probeStart = uint(thread.x / probeSideLength) * probeSideLength; + uint offset = probeSideLengthMinusOne - (thread.x % probeSideLength); + + uvec2 copyCoordinates = uvec2(probeStart + offset, (thread.y + 1)); + + vec4 load; + if (MODE == RADIANCE_MODE) + { + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + + thread.y += probeSideLengthMinusOne; + copyCoordinates = uvec2(probeStart + offset, thread.y - 1); + + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + } + else if (MODE == DISTANCE_MODE) + { + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + + thread.y += probeSideLengthMinusOne; + copyCoordinates = uvec2(probeStart + offset, thread.y - 1); + + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + } +} + +//------------------------------------------------------------------------------ +/** +*/ +void +BorderColumns(const uint MODE) +{ + const int NUM_TEXELS_PER_PROBE = int(MODE == RADIANCE_MODE ? NUM_IRRADIANCE_TEXELS_PER_PROBE : NUM_DISTANCE_TEXELS_PER_PROBE); + + uint probeSideLength = (NUM_TEXELS_PER_PROBE + 2); + uint probeSideLengthMinusOne = (probeSideLength - 1); + + uvec2 thread = gl_GlobalInvocationID.xy; + thread.x *= probeSideLength; + + uvec2 copyCoordinates = uvec2(0); + + int mod = int(gl_GlobalInvocationID.y % probeSideLength); + if (mod == 0 || mod == probeSideLengthMinusOne) + { + copyCoordinates.x = thread.x + NUM_TEXELS_PER_PROBE; + copyCoordinates.y = thread.y - sign(mod - 1) * NUM_TEXELS_PER_PROBE; + + vec4 load; + if (MODE == RADIANCE_MODE) + { + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + } + else if (MODE == DISTANCE_MODE) + { + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + } + + thread.x += probeSideLengthMinusOne; + copyCoordinates.x = thread.x - NUM_TEXELS_PER_PROBE; + + if (MODE == RADIANCE_MODE) + { + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + } + else if (MODE == DISTANCE_MODE) + { + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + } + return; + } + + uint probeStart = uint(thread.y / probeSideLength) * probeSideLength; + uint offset = probeSideLengthMinusOne - (thread.y % probeSideLength); + + copyCoordinates = uvec2(thread.x + 1, probeStart + offset); + + vec4 load; + if (MODE == RADIANCE_MODE) + { + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + } + else if (MODE == DISTANCE_MODE) + { + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + } + + thread.x += probeSideLengthMinusOne; + copyCoordinates = uvec2(thread.x - 1, probeStart + offset); + + if (MODE == RADIANCE_MODE) + { + load = imageLoad(IrradianceOutput, ivec2(copyCoordinates)); + imageStore(IrradianceOutput, ivec2(thread), load); + } + else if (MODE == DISTANCE_MODE) + { + load = imageLoad(DistanceOutput, ivec2(copyCoordinates)); + imageStore(DistanceOutput, ivec2(thread), load); + } +} + + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = NUM_IRRADIANCE_TEXELS_PER_PROBE +[local_size_y] = NUM_IRRADIANCE_TEXELS_PER_PROBE +[local_size_z] = 1 +shader void +ProbeFinalizeRadiance() +{ + Blend(RADIANCE_MODE); +} + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = NUM_DISTANCE_TEXELS_PER_PROBE +[local_size_y] = NUM_DISTANCE_TEXELS_PER_PROBE +[local_size_z] = 1 +shader void +ProbeFinalizeDistance() +{ + Blend(DISTANCE_MODE); +} + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 8 +[local_size_y] = 8 +[local_size_z] = 1 +shader void +ProbeFinalizeBorderRowsRadiance() +{ + BorderRows(RADIANCE_MODE); +} + + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 8 +[local_size_y] = 8 +[local_size_z] = 1 +shader void +ProbeFinalizeBorderColumnsRadiance() +{ + BorderColumns(RADIANCE_MODE); +} + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 8 +[local_size_y] = 8 +[local_size_z] = 1 +shader void +ProbeFinalizeBorderRowsDistance() +{ + BorderRows(DISTANCE_MODE); +} + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 8 +[local_size_y] = 8 +[local_size_z] = 1 +shader void +ProbeFinalizeBorderColumnsDistance() +{ + BorderColumns(DISTANCE_MODE); +} + +//------------------------------------------------------------------------------ +/** +*/ +program RadianceFinalize[string Mask = "ProbeFinalizeRadiance"; ] +{ + ComputeShader = ProbeFinalizeRadiance(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program DistanceFinalize[string Mask = "ProbeFinalizeDistance"; ] +{ + ComputeShader = ProbeFinalizeDistance(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program RadianceBorderRowsFixup[ string Mask = "ProbeFinalizeBorderRowsRadiance"; ] +{ + ComputeShader = ProbeFinalizeBorderRowsRadiance(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program RadianceBorderColumnsFixup[ string Mask = "ProbeFinalizeBorderColumnsRadiance"; ] +{ + ComputeShader = ProbeFinalizeBorderColumnsRadiance(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program DistanceBorderRowsFixup[ string Mask = "ProbeFinalizeBorderRowsDistance"; ] +{ + ComputeShader = ProbeFinalizeBorderRowsDistance(); +}; + +//------------------------------------------------------------------------------ +/** +*/ +program DistanceBorderColumnsFixup[ string Mask = "ProbeFinalizeBorderColumnsDistance"; ] +{ + ComputeShader = ProbeFinalizeBorderColumnsDistance(); +}; diff --git a/code/render/gi/shaders/probe_relocate_and_classify.fx b/code/render/gi/shaders/probe_relocate_and_classify.fx new file mode 100644 index 000000000..d086b8db6 --- /dev/null +++ b/code/render/gi/shaders/probe_relocate_and_classify.fx @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +// @file probe_relocate.fx +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ +#include +#include +#include + +group(SYSTEM_GROUP) read_write rgba16f image2D ProbeOffsetsOutput; +group(SYSTEM_GROUP) write r8 image2D ProbeStateOutput; + + +#include + +//------------------------------------------------------------------------------ +/** +*/ +[local_size_x] = 8 +[local_size_y] = 4 +[local_size_z] = 1 +shader void +ProbeRelocationAndClassify() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + int probeIndex = DDGIProbeIndex(texel, ProbeGridDimensions); + + int numProbes = ProbeGridDimensions.x * ProbeGridDimensions.y * ProbeGridDimensions.z; + if (probeIndex >= numProbes) + return; + + int probeRRIndex = (probeIndex < ProbeIndexStart) ? probeIndex + numProbes : probeIndex; + if (probeRRIndex >= ProbeIndexStart + ProbeIndexCount) + return; + + int storageProbeIndex; + if ((Options & SCROLL_OPTION) != 0) + storageProbeIndex = DDGIProbeIndexOffset(probeIndex, ProbeGridDimensions, ProbeScrollOffsets); + else + storageProbeIndex = probeIndex; + + ivec2 offsetTexelPosition = ivec2(DDGIProbeTexelPosition(storageProbeIndex, ProbeGridDimensions)); + + vec3 currentOffset = DDGIDecodeProbeOffsets(offsetTexelPosition, ProbeGridSpacing, ProbeOffsets); + + int closestBackfaceIndex = -1; + int closestFrontfaceIndex = -1; + int farthestFrontfaceIndex = -1; + float closestBackfaceDistance = 1e27f; + float closestFrontfaceDistance = 1e27f; + float farthestFrontfaceDistance = 0; + float backfaceCount = 0; + + int numRays = min(int(RaysPerProbe), int(DDGI_NUM_FIXED_RAYS)); + + for (int rayIndex = 0; rayIndex < numRays; rayIndex++) + { + ivec2 rayTexCoord = ivec2(rayIndex, probeIndex); + + float hitDistance = fetch2D(ProbeRadiance, Basic2DSampler, rayTexCoord, 0).w; + + if ((Options & RELOCATION_OPTION) != 0) + { + if (hitDistance < 0.0f) + { + backfaceCount++; + hitDistance = hitDistance * -5.0f; + if (hitDistance < closestBackfaceDistance) + { + closestBackfaceDistance = hitDistance; + closestBackfaceIndex = rayIndex; + } + } + else + { + if (hitDistance < closestFrontfaceDistance) + { + closestFrontfaceDistance = hitDistance; + closestFrontfaceIndex = rayIndex; + } + else if (hitDistance > farthestFrontfaceDistance) + { + farthestFrontfaceDistance = hitDistance; + farthestFrontfaceIndex = rayIndex; + } + } + } + else + { + if (hitDistance < 0.0f) + { + backfaceCount++; + continue; + } + closestFrontfaceDistance = min(closestFrontfaceDistance, hitDistance); + } + } + + if ((Options & RELOCATION_OPTION) != 0) + { + vec3 fulloffset = vec3(1e27); + if (closestBackfaceIndex != -1 && (float(backfaceCount) / numRays) > BackfaceThreshold) + { + vec3 closestBackfaceDirection = closestBackfaceDistance * normalize(DDGIGetProbeDirection(closestBackfaceIndex, TemporalRotation, Options)); + fulloffset = currentOffset + closestBackfaceDirection * (ProbeDistanceScale + 1.0f); + } + else if (closestFrontfaceDistance < MinFrontfaceDistance) + { + vec3 closestFrontfaceDirection = DDGIGetProbeDirection(closestFrontfaceIndex, TemporalRotation, Options); + vec3 farthestFrontfaceDirection = DDGIGetProbeDirection(farthestFrontfaceIndex, TemporalRotation, Options); + + if (dot(closestFrontfaceDirection, farthestFrontfaceDirection) <= 0.f) + { + farthestFrontfaceDistance *= min(farthestFrontfaceDistance, 1.0f); + + fulloffset = currentOffset + farthestFrontfaceDirection * ProbeDistanceScale; + } + } + else if (closestFrontfaceDistance > MinFrontfaceDistance + ProbeDistanceScale) + { + float moveBackMargin = min(closestFrontfaceDistance - MinFrontfaceDistance, length(currentOffset)); + vec3 moveBackDirection = normalize(-currentOffset); + fulloffset = currentOffset + (moveBackMargin * moveBackDirection); + } + + vec3 normalizedOffset = fulloffset / ProbeGridSpacing; + if (dot(normalizedOffset, normalizedOffset) < 0.2025f) + { + currentOffset = fulloffset; + } + + imageStore(ProbeOffsetsOutput, offsetTexelPosition, vec4(currentOffset / ProbeGridSpacing, 0.0f)); + } + + if ((Options & CLASSIFICATION_OPTION) != 0) + { + vec3 geometryBounds = ProbeGridSpacing * 2; + if (all(lessThanEqual(vec3(closestFrontfaceDistance), geometryBounds)) && (float(backfaceCount) / numRays) < BackfaceThreshold) + { + imageStore(ProbeStateOutput, offsetTexelPosition, vec4(PROBE_STATE_ACTIVE)); + } + else + { + imageStore(ProbeStateOutput, offsetTexelPosition, vec4(PROBE_STATE_INACTIVE)); + } + } +} + +//------------------------------------------------------------------------------ +/** +*/ +program ProbeRelocate[string Mask = "ProbeRelocateAndClassify"; ] +{ + ComputeShader = ProbeRelocationAndClassify(); +}; diff --git a/code/render/gi/shaders/probe_shared.fxh b/code/render/gi/shaders/probe_shared.fxh new file mode 100644 index 000000000..4071de756 --- /dev/null +++ b/code/render/gi/shaders/probe_shared.fxh @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// @file probe_shared.fxh +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ + +#ifndef PROBE_SHARED_FXH +#define PROBE_SHARED_FXH +#include +group(SYSTEM_GROUP) constant VolumeConstants +{ + mat4x4 TemporalRotation; + + vec3 Scale; + uint Options; + + vec3 Offset; + int NumIrradianceTexels; + + ivec3 ProbeGridDimensions; + int ProbeIndexStart; + + ivec3 ProbeScrollOffsets; + int ProbeIndexCount; + + vec4 Rotation; + + vec3 ProbeGridSpacing; + int NumDistanceTexels; + + vec4 MinimalDirections[32]; + vec4 ExtraDirections[1024-32]; + vec4 Directions[1024]; + + float InverseGammaEncoding; + float Hysteresis; + float IrradianceGamma; + uint RaysPerProbe; + + float NormalBias; + float ViewBias; + float IrradianceScale; + float DistanceExponent; + + float ChangeThreshold; + float BrightnessThreshold; + float BackfaceThreshold; + float ProbeDistanceScale; + + float MinFrontfaceDistance; + uint ProbeIrradiance; + uint ProbeDistances; + uint ProbeOffsets; + + uint ProbeStates; + uint ProbeScrollSpace; + uint ProbeRadiance; + + float DebugSize; +}; + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIGetProbeDirection(int rayIndex, mat4x4 rotation, uint options) +{ + vec3 direction; + if ((options & (RELOCATION_OPTION | CLASSIFICATION_OPTION)) != 0) + { + bool useFixedRays = rayIndex < DDGI_NUM_FIXED_RAYS; + uint adjustedRayIndex = useFixedRays ? rayIndex : rayIndex - DDGI_NUM_FIXED_RAYS; + direction = useFixedRays ? MinimalDirections[adjustedRayIndex].xyz : ExtraDirections[adjustedRayIndex].xyz; + if (useFixedRays) + { + return direction; + } + } + else + { + direction = Directions[rayIndex].xyz; + } + return normalize((rotation * vec4(direction, 0)).xyz); +} + +//------------------------------------------------------------------------------ +/** +*/ +uvec2 +DDGIThreadBaseCoord(int probeIndex, ivec3 probeGridCounts, int probeNumTexels) +{ + int probesPerPlane = DDGIProbesPerPlane(probeGridCounts); + int planeIndex = probeIndex / probesPerPlane; + int probeIndexInPlane = probeIndex % probesPerPlane; + + int planeWidthInProbes = probeGridCounts.x; + + ivec2 probeCoordInPlane = ivec2(probeIndexInPlane % planeWidthInProbes, probeIndexInPlane / planeWidthInProbes); + int baseCoordX = (planeWidthInProbes * planeIndex + probeCoordInPlane.x) * probeNumTexels; + int baseCoordY = probeCoordInPlane.y * probeNumTexels; + return uvec2(baseCoordX, baseCoordY); +} + +#endif // PROBE_SHARED_FXH diff --git a/code/render/gi/shaders/probe_update.fx b/code/render/gi/shaders/probe_update.fx new file mode 100644 index 000000000..3606ed262 --- /dev/null +++ b/code/render/gi/shaders/probe_update.fx @@ -0,0 +1,234 @@ +//------------------------------------------------------------------------------ +// @file probe_update.fx +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include + +group(SYSTEM_GROUP) write rgba32f image2D RadianceOutput; + + + +#include "probe_shared.fxh" + +//------------------------------------------------------------------------------ +/** +*/ +ivec2 +GetOutputPixel(uvec2 rayIndex, uint numSamples) +{ + uint row = rayIndex.x / numSamples; + uint column = (rayIndex.x % numSamples) * numSamples + rayIndex.y; + return ivec2(row, column); +} + +//------------------------------------------------------------------------------ +/** +*/ +ivec2 +GetProbeTexel(int probe, int3 probeGridDimensions) +{ + return ivec2(probe % (probeGridDimensions.x * probeGridDimensions.y), probe / (probeGridDimensions.x * probeGridDimensions.y)); +} + +//------------------------------------------------------------------------------ +/** +*/ +uint +FloatToUInt(float v, float scale) +{ + return uint(floor(v * scale + 0.5f)); +} + +//------------------------------------------------------------------------------ +/** +*/ +uint +PackFloat3ToUInt(vec3 val) +{ + return FloatToUInt(val.x, 1023.0f) | (FloatToUInt(val.y, 1023.0f) << 10) | (FloatToUInt(val.z, 1023.0f) << 20); +} + +//------------------------------------------------------------------------------ +/** +*/ +void +StoreRadianceAndDepth(ivec2 coordinate, vec3 radiance, float depth) +{ + const float ValueThreshold = 1 / 255.0f; + if (DDGIMaxComponent(radiance) <= ValueThreshold) + radiance = vec3(0); + radiance *= IrradianceScale; +#ifdef USE_COMPRESSED_RADIANCE + imageStore(RadianceOutput, coordinate, vec4( + uintBitsToFloat( + PackFloat3ToUInt( + clamp(radiance, vec3(0.0f), vec3(1.0f)) + ) + ), depth, 0.0f, 0.0f)); +#else + imageStore(RadianceOutput, coordinate, vec4(radiance, depth)); +#endif +} + +//------------------------------------------------------------------------------ +/** +*/ +shader void +RayGen( + [ray_payload] out LightResponsePayload payload +) +{ + //payload.radiance = vec3(0); + payload.normal = vec3(0, 1, 0); + int rayIndex = int(gl_LaunchIDEXT.x); + int probeIndex = int(gl_LaunchIDEXT.y); + + int storageProbeIndex; + if ((Options & SCROLL_OPTION) != 0) + { + storageProbeIndex = DDGIProbeIndexOffset(probeIndex, ProbeGridDimensions, ProbeScrollOffsets); + } + else + { + storageProbeIndex = probeIndex; + } + ivec2 texelPosition = DDGIProbeTexelPosition(storageProbeIndex, ProbeGridDimensions); + + int numProbes = ProbeGridDimensions.x * ProbeGridDimensions.y * ProbeGridDimensions.z; + int probeRRIndex = (probeIndex < ProbeIndexStart) ? probeIndex + numProbes : probeIndex; + if (probeRRIndex >= ProbeIndexStart + ProbeIndexCount) + return; + + int state = floatBitsToInt(fetch2D(ProbeStates, Basic2DSampler, texelPosition, 0).x); + if ((Options & CLASSIFICATION_OPTION) != 0) + { + if (state == PROBE_STATE_INACTIVE && rayIndex >= DDGI_NUM_FIXED_RAYS) + { + return; + } + } + + vec3 probeWorldPosition; + if ((Options & RELOCATION_OPTION) != 0) + { + if ((Options & SCROLL_OPTION) != 0) + { + probeWorldPosition = DDGIProbeWorldPositionWithScrollAndOffset(probeIndex, Offset, Rotation, ProbeGridDimensions, ProbeGridSpacing, ProbeScrollOffsets, ProbeOffsets); + } + else + { + probeWorldPosition = DDGIProbeWorldPositionWithOffset(probeIndex, Offset, Rotation, ProbeGridDimensions, ProbeGridSpacing, ProbeOffsets); + } + } + else + { + probeWorldPosition = DDGIProbeWorldPosition(probeIndex, Offset, Rotation, ProbeGridDimensions, ProbeGridSpacing); + } + + vec3 probeRayDirection = DDGIGetProbeDirection(rayIndex, TemporalRotation, Options); + + const float MaxDistance = 10000.0f; + payload.bits = 0x0; + payload.albedo = vec3(0); + payload.material = vec4(0); + payload.radiance = vec3(0); + payload.alpha = 0.0f; + payload.normal = vec3(0); + payload.depth = 0; + + traceRayEXT(TLAS, gl_RayFlagsNoneEXT , 0xff, 0, 0, 0, probeWorldPosition, 0.01f, probeRayDirection, MaxDistance, 0); + if ((payload.bits & RAY_MISS_BIT) != 0) + { + vec3 lightDir = normalize(GlobalLightDirWorldspace.xyz); + vec3 dir = normalize(probeRayDirection); + vec3 atmo = CalculateAtmosphericScattering(dir, GlobalLightDirWorldspace.xyz) * GlobalLightColor.rgb; + StoreRadianceAndDepth(ivec2(gl_LaunchIDEXT.xy), atmo, 1e27f); + return; + } + + // If hit is back face and it's not a 2 sided material two-sided material + if ((payload.bits & (RAY_BACK_FACE_BIT | RAY_MATERIAL_TWO_SIDED_BIT)) == RAY_BACK_FACE_BIT) + { + StoreRadianceAndDepth(ivec2(gl_LaunchIDEXT.xy), vec3(0), -payload.depth * 0.2f); + return; + + } + + // If probe is inactive, store depth in case it needs to be reactivated + if ((Options & CLASSIFICATION_OPTION) != 0 && state == PROBE_STATE_INACTIVE) + { + StoreRadianceAndDepth(ivec2(gl_LaunchIDEXT.xy), vec3(0), payload.depth); + return; + } + + // TODO: Calculate light + // If all conditions fall through, light the probe + vec3 probeLighting = vec3(0); + vec3 albedo = payload.albedo - payload.albedo * payload.material[MAT_METALLIC]; + + GIVolume volumeArg; + volumeArg.Offset = Offset; + volumeArg.Rotation = Rotation; + volumeArg.GridCounts = ProbeGridDimensions; + volumeArg.GridSpacing = ProbeGridSpacing; + volumeArg.ScrollOffsets = ProbeScrollOffsets; + volumeArg.NumIrradianceTexels = NumIrradianceTexels; + volumeArg.NumDistanceTexels = NumDistanceTexels; + volumeArg.EncodingGamma = IrradianceGamma; + volumeArg.Irradiance = ProbeIrradiance; + volumeArg.Distances = ProbeDistances; + volumeArg.Offsets = ProbeOffsets; + volumeArg.States = ProbeStates; + volumeArg.NormalBias = NormalBias; + volumeArg.ViewBias = ViewBias; + volumeArg.IrradianceScale = IrradianceScale; + volumeArg.Options = Options; + + vec3 worldSpacePos = probeWorldPosition + probeRayDirection * payload.depth; + + vec3 light = payload.radiance; + + vec3 relativePos = abs(worldSpacePos - Offset); + if (relativePos.x > Scale.x || relativePos.y > Scale.y || relativePos.z > Scale.z) + { + probeLighting = vec3(0); + } + else + { + vec3 surfaceBias = DDGISurfaceBias(payload.normal, probeRayDirection, NormalBias, ViewBias); + vec3 irradiance = EvaluateDDGIIrradiance(worldSpacePos, surfaceBias, payload.normal, volumeArg, Options); + + float maxAlbedo = 0.9f; + probeLighting = irradiance * (min(albedo, maxAlbedo) / PI); + probeLighting /= IrradianceScale; + } + + StoreRadianceAndDepth(ivec2(gl_LaunchIDEXT.xy), max(vec3(0), light + probeLighting), payload.depth); +} + +//------------------------------------------------------------------------------ +/** +*/ +shader void +Miss( + [ray_payload] in LightResponsePayload payload +) +{ + payload.bits |= RAY_MISS_BIT; +} + + +//------------------------------------------------------------------------------ +/** +*/ +program Main[string Mask = "ProbeRayGen"; ] +{ + RayGenerationShader = RayGen(); + RayMissShader = Miss(); +}; diff --git a/code/render/gi/shaders/probeupdate.fx b/code/render/gi/shaders/probeupdate.fx deleted file mode 100644 index 2ec082862..000000000 --- a/code/render/gi/shaders/probeupdate.fx +++ /dev/null @@ -1,96 +0,0 @@ -//------------------------------------------------------------------------------ -// @file probeupdate.fx -// @copyright (C) 2024 Individual contributors, see AUTHORS file -//------------------------------------------------------------------------------ - -#include -#include -#include -#include -#include "ddgi.fxh" - -group(BATCH_GROUP) write rgba8 image2D RadianceOutput; -group(BATCH_GROUP) write rgba8 image2D NormalOutput; -group(BATCH_GROUP) write r32f image2D DepthOutput; - -struct Probe -{ - vec3 position; - mat3 rotation; -}; - -group(BATCH_GROUP) rw_buffer ProbeBuffer -{ - Probe Probes[]; -}; - -group(BATCH_GROUP) constant SampleDirections -{ - vec3 Directions[24]; -}; - -//------------------------------------------------------------------------------ -/** -*/ -shader void -RayGen( - [ray_payload] out HitResult payload -) -{ - //payload.radiance = vec3(0); - payload.normal = vec3(0, 1, 0); - - Probe probe = Probes[gl_LaunchIDEXT.x]; - vec3 direction = Directions[gl_LaunchIDEXT.y] * probe.rotation; - - const float MaxDistance = 10000.0f; - const uint NumColorSamples = 16; - const uint NumDepthSamples = 8; - - traceRayEXT(TLAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, probe.position.xyz, 0.01f, direction, MaxDistance, 0); - vec3 WorldSpacePos = probe.position.xyz + direction.xyz * payload.depth; - - // If launch ID is below 16, it's a color sample - if (gl_LaunchIDEXT.y < NumColorSamples) - { - uint row = gl_LaunchIDEXT.y / NumColorSamples; - uint column = (gl_LaunchIDEXT.y % NumColorSamples) * NumColorSamples + gl_LaunchIDEXT.x; - - vec3 light = CalculateLightRT(WorldSpacePos, payload.depth / 10000.0f, payload.albedo.rgb, payload.material, payload.normal); - imageStore(RadianceOutput, ivec2(row, column), vec4(light, 0)); - imageStore(NormalOutput, ivec2(row, column), vec4(payload.normal, 0)); - } - else - { - uint row = gl_LaunchIDEXT.y / NumDepthSamples; - uint column = (gl_LaunchIDEXT.y % NumDepthSamples) * NumDepthSamples + gl_LaunchIDEXT.x; - imageStore(DepthOutput, ivec2(row, column), vec4(payload.depth / MaxDistance)); - } - - // TODO use octahedral mapping to output the probe pixels here -} - -//------------------------------------------------------------------------------ -/** -*/ -shader void -Miss( - [ray_payload] in HitResult payload -) -{ - vec3 lightDir = normalize(GlobalLightDirWorldspace.xyz); - vec3 dir = normalize(gl_WorldRayDirectionEXT); - vec3 atmo = CalculateAtmosphericScattering(dir, GlobalLightDirWorldspace.xyz) * GlobalLightColor.rgb; - - //payload.radiance = atmo; - //payload.normal = -gl_WorldRayDirectionEXT; -} - -//------------------------------------------------------------------------------ -/** -*/ -program Main[string Mask = "ProbeRayGen"; ] -{ - RayGenerationShader = RayGen(); - RayMissShader = Miss(); -}; diff --git a/code/render/graphics/globalconstants.cc b/code/render/graphics/globalconstants.cc index 177dbb76a..65b320082 100644 --- a/code/render/graphics/globalconstants.cc +++ b/code/render/graphics/globalconstants.cc @@ -28,7 +28,7 @@ struct int bits; }; DirtySet tickParamsDirty; - IndexT tickCboOffset, viewCboOffset, shadowViewCboOffset; + uint64 tickCboOffset, viewCboOffset, shadowViewCboOffset; Shared::ViewConstants viewConstants; Shared::ShadowViewConstants shadowViewConstants; @@ -53,7 +53,7 @@ CreateGlobalConstants(const GlobalConstantsCreateInfo& info) { state.frameResourceTables[i] = CoreGraphics::ShaderCreateResourceTable(shader, NEBULA_FRAME_GROUP, state.frameResourceTables.Size()); CoreGraphics::ObjectSetName(state.frameResourceTables[i], "Main Frame Group Descriptor"); - + state.tickResourceTables[i] = CoreGraphics::ShaderCreateResourceTable(shader, NEBULA_TICK_GROUP, state.tickResourceTables.Size()); CoreGraphics::ObjectSetName(state.tickResourceTables[i], "Main Tick Group Descriptor"); } @@ -85,12 +85,12 @@ AllocateGlobalConstants() IndexT bufferedFrameIndex = CoreGraphics::GetBufferedFrameIndex(); // Bind tables with memory allocated - ResourceTableSetConstantBuffer(state.frameResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Frame::ViewConstants_SLOT, 0, sizeof(Shared::ViewConstants), (SizeT)state.viewCboOffset }); - ResourceTableSetConstantBuffer(state.frameResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Frame::ShadowViewConstants_SLOT, 0, sizeof(Shared::ShadowViewConstants), (SizeT)state.shadowViewCboOffset }); + ResourceTableSetConstantBuffer(state.frameResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Frame::ViewConstants_SLOT, 0, sizeof(Shared::ViewConstants), state.viewCboOffset }); + ResourceTableSetConstantBuffer(state.frameResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Frame::ShadowViewConstants_SLOT, 0, sizeof(Shared::ShadowViewConstants), state.shadowViewCboOffset }); ResourceTableCommitChanges(state.frameResourceTables[bufferedFrameIndex]); // Update tick resource tables - ResourceTableSetConstantBuffer(state.tickResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Tick::PerTickParams_SLOT, 0, sizeof(Shared::PerTickParams), (SizeT)state.tickCboOffset }); + ResourceTableSetConstantBuffer(state.tickResourceTables[bufferedFrameIndex], { CoreGraphics::GetConstantBuffer(bufferedFrameIndex), Shared::Table_Tick::PerTickParams_SLOT, 0, sizeof(Shared::PerTickParams), state.tickCboOffset }); ResourceTableCommitChanges(state.tickResourceTables[bufferedFrameIndex]); } @@ -200,7 +200,7 @@ FlushUpdates(const CoreGraphics::CmdBufferId buf, const CoreGraphics::QueueType /** */ void -GetOffsets(IndexT& tickOffset, IndexT& viewOffset, IndexT& shadowOffset) +GetOffsets(uint64& tickOffset, uint64& viewOffset, uint64& shadowOffset) { tickOffset = state.tickCboOffset; viewOffset = state.viewCboOffset; diff --git a/code/render/graphics/globalconstants.h b/code/render/graphics/globalconstants.h index d17260b53..668bf71a6 100644 --- a/code/render/graphics/globalconstants.h +++ b/code/render/graphics/globalconstants.h @@ -36,7 +36,7 @@ void UpdateShadowConstants(const Shared::ShadowViewConstants& shadowViewConstant void FlushUpdates(const CoreGraphics::CmdBufferId buf, const CoreGraphics::QueueType queue); /// Get frame constant offsets -void GetOffsets(IndexT& tickOffset, IndexT& viewOffset, IndexT& shadowOffset); +void GetOffsets(uint64& tickOffset, uint64& viewOffset, uint64& shadowOffset); /// Get tick params constant buffer const Shared::PerTickParams& GetTickParams(); diff --git a/code/render/lighting/lightcontext.cc b/code/render/lighting/lightcontext.cc index f4f7f590e..fc81780c4 100644 --- a/code/render/lighting/lightcontext.cc +++ b/code/render/lighting/lightcontext.cc @@ -82,6 +82,7 @@ struct // these are used to update the light clustering alignas(16) LightsCluster::LightLists lightList; + LightsCluster::LightUniforms consts; } clusterState; @@ -239,7 +240,7 @@ LightContext::Create() }); // Bind shadows - FrameScript_default::Bind_ClusterLightList(clusterState.clusterLightsList); + FrameScript_default::Bind_LightList(clusterState.clusterLightsList); FrameScript_default::Bind_ClusterLightIndexLists(clusterState.clusterLightIndexLists); FrameScript_default::RegisterSubgraph_LightsCopy_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) { @@ -248,7 +249,7 @@ LightContext::Create() to.offset = 0; CmdCopy(cmdBuf, clusterState.stagingClusterLightsList.buffers[bufferIndex], { from }, clusterState.clusterLightsList, { to }, sizeof(LightsCluster::LightLists)); }, { - { FrameScript_default::BufferIndex::ClusterLightList, CoreGraphics::PipelineStage::TransferWrite } + { FrameScript_default::BufferIndex::LightList, CoreGraphics::PipelineStage::TransferWrite } }); FrameScript_default::RegisterSubgraph_LightsCull_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) @@ -259,7 +260,7 @@ LightContext::Create() std::array dimensions = Clustering::ClusterContext::GetClusterDimensions(); CmdDispatch(cmdBuf, Math::ceil((dimensions[0] * dimensions[1] * dimensions[2]) / 64.0f), 1, 1); }, { - { FrameScript_default::BufferIndex::ClusterLightList, CoreGraphics::PipelineStage::ComputeShaderRead } + { FrameScript_default::BufferIndex::LightList, CoreGraphics::PipelineStage::ComputeShaderRead } , { FrameScript_default::BufferIndex::ClusterLightIndexLists, CoreGraphics::PipelineStage::ComputeShaderWrite } , { FrameScript_default::BufferIndex::ClusterBuffer, CoreGraphics::PipelineStage::ComputeShaderRead } }); @@ -297,8 +298,6 @@ LightContext::SetupGlobalLight( const Math::vec3& color, const float intensity, const Math::vec3& ambient, - const Math::vec3& backlight, - const float backlightFactor, const float zenith, const float azimuth, bool castShadows) @@ -319,9 +318,7 @@ LightContext::SetupGlobalLight( Math::mat4 mat = lookatrh(Math::point(0.0f), sunPosition, Math::vector::upvec()); SetGlobalLightTransform(cid, mat, Math::xyz(sunPosition)); - directionalLightAllocator.Get(lid) = backlight; directionalLightAllocator.Get(lid) = ambient; - directionalLightAllocator.Get(lid) = backlightFactor; if (castShadows && shadowCasterAllocator.Size() < 16) { @@ -525,10 +522,10 @@ LightContext::SetupAreaLight( const MaterialTemplates::MaterialTemplateValue& value = MaterialTemplates::base::__AreaLight.__EmissiveColor; void* mem = Materials::MaterialLoader::AllocateConstantMemory(value.GetSize()); - MaterialInterfaces::ArealightMaterial* data = (MaterialInterfaces::ArealightMaterial*)StackAlloc(matTemplate->bufferSize); + MaterialInterfaces::ArealightMaterial* data = ArrayAllocStack(1); (color * intensity).store(data->EmissiveColor); Materials::MaterialSetConstants(material, data, sizeof(MaterialInterfaces::ArealightMaterial)); - StackFree(data); + ArrayFreeStack(1, data); CoreGraphics::MeshId mesh; switch (shape) @@ -1012,6 +1009,15 @@ LightContext::GetLightsBuffer() return clusterState.clusterLightsList; } +//------------------------------------------------------------------------------ +/** +*/ +const LightsCluster::LightUniforms& +LightContext::GetLightUniforms() +{ + return clusterState.consts; +} + //------------------------------------------------------------------------------ /** */ @@ -1052,10 +1058,7 @@ LightContext::UpdateViewDependentResources(const Ptr& view, cons Shared::PerTickParams params = Graphics::GetTickParams(); (genericLightAllocator.Get(cid.id) * genericLightAllocator.Get(cid.id)).store(params.GlobalLightColor); directionalLightAllocator.Get(globalLightId).store(params.GlobalLightDirWorldspace); - directionalLightAllocator.Get(globalLightId).store(params.GlobalBackLightColor); directionalLightAllocator.Get(globalLightId).store(params.GlobalAmbientLightColor); - Math::vec4 viewSpaceLightDir = viewTransform * Math::vec4(directionalLightAllocator.Get(globalLightId), 0.0f); - params.GlobalBackLightOffset = directionalLightAllocator.Get(globalLightId); params.ltcLUT0 = CoreGraphics::TextureGetBindlessHandle(textureState.ltcLut0); params.ltcLUT1 = CoreGraphics::TextureGetBindlessHandle(textureState.ltcLut1); @@ -1065,6 +1068,7 @@ LightContext::UpdateViewDependentResources(const Ptr& view, cons if (genericLightAllocator.Get(cid.id)) { params.GlobalLightShadowBuffer = CoreGraphics::TextureGetBindlessHandle(lightServerState.globalLightShadowMap); + params.EnableTerrainShadows = lightServerState.terrainShadowMap == CoreGraphics::InvalidTextureId ? 1 : 0; params.TerrainShadowBuffer = CoreGraphics::TextureGetBindlessHandle(lightServerState.terrainShadowMap); params.TerrainShadowMapSize[0] = params.TerrainShadowMapSize[1] = lightServerState.terrainShadowMapSize; params.InvTerrainSize[0] = params.InvTerrainSize[1] = 1.0f / Math::max(1u, lightServerState.terrainSize); @@ -1217,12 +1221,13 @@ LightContext::UpdateViewDependentResources(const Ptr& view, cons float height = shape == AreaLightShape::Tube ? 1.0f : scale.y; trans.setscale(Math::vector(width * range[i], height * range[i], twoSided ? range[i] * 2 : -range[i])); trans.setposition(trans.getposition() + Math::vector(0, 0, twoSided ? 0 : -range[i] / 2)); - Math::mat4 viewSpace = viewTransform * trans.getmatrix(); - Math::bbox bbox(viewSpace); - bbox.pmin.store3(areaLight.bboxMin); + trans.getmatrix().position.store3(areaLight.center); + Math::bbox box = trans.getmatrix(); + //Math::vec3 localExtents = Math::vec3((abs(trans.getscale().x) + abs(trans.getscale().y) + abs(trans.getscale().z)) * 0.5f); + (box.extents() * 2).store(areaLight.extents); + areaLight.range = range[i]; - bbox.pmax.store3(areaLight.bboxMax); areaLight.radius = scale.y; uint flags = 0; @@ -1277,14 +1282,14 @@ LightContext::UpdateViewDependentResources(const Ptr& view, cons // get per-view resource tables CoreGraphics::ResourceTableId frameResourceTable = Graphics::GetFrameResourceTable(bufferIndex); - LightsCluster::LightUniforms consts; - consts.NumSpotLights = numSpotLights; - consts.NumPointLights = numPointLights; - consts.NumAreaLights = numAreaLights; - consts.NumLightClusters = Clustering::ClusterContext::GetNumClusters(); - consts.SSAOBuffer = CoreGraphics::TextureGetBindlessHandle(FrameScript_default::Texture_SSAOBuffer()); - IndexT offset = SetConstants(consts); - ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::LightUniforms_SLOT, 0, sizeof(Shared::LightUniforms), (SizeT)offset }); + + clusterState.consts.NumSpotLights = numSpotLights; + clusterState.consts.NumPointLights = numPointLights; + clusterState.consts.NumAreaLights = numAreaLights; + clusterState.consts.NumLightClusters = Clustering::ClusterContext::GetNumClusters(); + clusterState.consts.SSAOBuffer = CoreGraphics::TextureGetBindlessHandle(FrameScript_default::Texture_SSAOBuffer()); + uint64 offset = SetConstants(clusterState.consts); + ResourceTableSetConstantBuffer(frameResourceTable, { GetConstantBuffer(bufferIndex), Shared::Table_Frame::LightUniforms_SLOT, 0, sizeof(Shared::LightUniforms), offset }); ResourceTableCommitChanges(frameResourceTable); Combine::CombineUniforms combineConsts; @@ -1292,7 +1297,7 @@ LightContext::UpdateViewDependentResources(const Ptr& view, cons combineConsts.LowresResolution[0] = 1.0f / lightDims.width; combineConsts.LowresResolution[1] = 1.0f / lightDims.height; offset = SetConstants(combineConsts); - ResourceTableSetConstantBuffer(combineState.resourceTables[bufferIndex], { GetConstantBuffer(bufferIndex), Combine::Table_Batch::CombineUniforms_SLOT, 0, sizeof(Combine::CombineUniforms), (SizeT)offset }); + ResourceTableSetConstantBuffer(combineState.resourceTables[bufferIndex], { GetConstantBuffer(bufferIndex), Combine::Table_Batch::CombineUniforms_SLOT, 0, sizeof(Combine::CombineUniforms), offset }); ResourceTableCommitChanges(combineState.resourceTables[bufferIndex]); } diff --git a/code/render/lighting/lightcontext.h b/code/render/lighting/lightcontext.h index c6d494a1c..4e89d2e60 100644 --- a/code/render/lighting/lightcontext.h +++ b/code/render/lighting/lightcontext.h @@ -12,6 +12,7 @@ #include "coregraphics/buffer.h" #include "coregraphics/texture.h" #include +#include namespace Frame { @@ -51,7 +52,7 @@ class LightContext : public Graphics::GraphicsContext static void Discard(); /// setup entity as global light - static void SetupGlobalLight(const Graphics::GraphicsEntityId id, const Math::vec3& color, const float intensity, const Math::vec3& ambient, const Math::vec3& backlight, const float backlightFactor, const float zenith, const float azimuth, bool castShadows = false); + static void SetupGlobalLight(const Graphics::GraphicsEntityId id, const Math::vec3& color, const float intensity, const Math::vec3& ambient, const float zenith, const float azimuth, bool castShadows = false); /// Setup entity as point light source static void SetupPointLight( const Graphics::GraphicsEntityId id @@ -150,6 +151,9 @@ class LightContext : public Graphics::GraphicsContext static const CoreGraphics::BufferId GetLightIndexBuffer(); /// get light lists buffer static const CoreGraphics::BufferId GetLightsBuffer(); + /// get light uniforms + static const LightsCluster::LightUniforms& GetLightUniforms(); + private: /// Set global light transform @@ -253,8 +257,6 @@ class LightContext : public Graphics::GraphicsContext enum { DirectionalLight_Direction, - DirectionalLight_Backlight, - DirectionalLight_BacklightOffset, DirectionalLight_Ambient, DirectionalLight_Transform, DirectionalLight_ViewProjTransform, @@ -263,8 +265,6 @@ class LightContext : public Graphics::GraphicsContext typedef Ids::IdAllocator< Math::vector, // direction - Math::vec3, // backlight color - float, // backlight offset Math::vec3, // ambient Math::mat4, // transform (basically just a rotation in the direction) Math::mat4, // transform for visibility and such diff --git a/code/render/materials/material.cc b/code/render/materials/material.cc index 3d21ab3f4..9f4876d09 100644 --- a/code/render/materials/material.cc +++ b/code/render/materials/material.cc @@ -7,6 +7,7 @@ #include "shaderconfig.h" #include "resources/resourceserver.h" #include "materials/materialtemplates.h" + namespace Materials { @@ -120,7 +121,7 @@ CreateMaterial(const MaterialTemplates::Entry* entry) if (AllBits(instanceGroupMask, 1 << it)) { IndexT slot = it; - SizeT bufSize = CoreGraphics::ShaderGetConstantBufferSize(shader, NEBULA_INSTANCE_GROUP, CoreGraphics::ShaderCalculateConstantBufferIndex(instanceGroupMask, it)); + uint64 bufSize = CoreGraphics::ShaderGetConstantBufferSize(shader, NEBULA_INSTANCE_GROUP, CoreGraphics::ShaderCalculateConstantBufferIndex(instanceGroupMask, it)); IndexT bufferIndex = 0; for (const auto& table : instanceTables) CoreGraphics::ResourceTableSetConstantBuffer(table, { CoreGraphics::GetConstantBuffer(bufferIndex++), slot, 0, bufSize, 0, false, true }); @@ -137,7 +138,7 @@ CreateMaterial(const MaterialTemplates::Entry* entry) { CoreGraphics::TextureId tex = Resources::CreateResource(texture->def->resource, "materials", nullptr, nullptr, true, false); CoreGraphics::ResourceTableSetTexture(surfaceTable, { tex, texture->slot }); - } + } } // Finish off by comitting all table changes @@ -234,6 +235,14 @@ MaterialSetConstants(const MaterialId mat, const void* data, const uint size) const auto& buf = materialAllocator.Get(mat.id); CoreGraphics::BufferUpdate(buf, data, size); CoreGraphics::BufferFlush(buf); + + IndexT materialBufferBinding = Materials::MaterialGetBufferBinding(mat); + const MaterialBindlessBufferBinding& bindlessBinding = Materials::MaterialGetBindlessForEditor(mat); + if (bindlessBinding.buffer != nullptr) + { + memcpy(bindlessBinding.buffer, data, size); + *bindlessBinding.dirtyFlag = true; + } } //------------------------------------------------------------------------------ @@ -325,6 +334,25 @@ MaterialGetSortCode(const MaterialId mat) } #ifdef WITH_NEBULA_EDITOR +//------------------------------------------------------------------------------ +/** +*/ +void +MaterialBindlessForEditor(const MaterialId mat, char* buf, bool* dirtyFlag) +{ + MaterialBindlessBufferBinding binding{buf, dirtyFlag}; + materialAllocator.Set(mat.id, binding); +} + +//------------------------------------------------------------------------------ +/** +*/ +const MaterialBindlessBufferBinding& +MaterialGetBindlessForEditor(const MaterialId mat) +{ + return materialAllocator.Get(mat.id); +} + //------------------------------------------------------------------------------ /** */ @@ -415,7 +443,8 @@ MaterialInstanceApply(const MaterialInstanceId id, const CoreGraphics::CmdBuffer // Set instance table CoreGraphics::ConstantBufferOffset offset = materialInstanceAllocator.Get(id.instance); - CoreGraphics::CmdSetResourceTable(buf, table, NEBULA_INSTANCE_GROUP, CoreGraphics::GraphicsPipeline, 1, &offset); + n_assert((offset & 0xFFFFFFFF00000000) == 0x0); + CoreGraphics::CmdSetResourceTable(buf, table, NEBULA_INSTANCE_GROUP, CoreGraphics::GraphicsPipeline, { (uint)offset }); } } diff --git a/code/render/materials/material.h b/code/render/materials/material.h index 51c7aeb46..71c75a701 100644 --- a/code/render/materials/material.h +++ b/code/render/materials/material.h @@ -32,6 +32,20 @@ ID_32_24_8_NAMED_TYPE(MaterialInstanceId, instance, materialId, materialGenerati typedef IndexT BatchIndex; +enum BindlessBufferDirtyBits +{ + Graphics = 0x1, // Bindless material buffers needs a graphics queue update + Compute = 0x2, // Bindless material buffers needs a compute queue update + All = Graphics | Compute +}; + +#ifdef WITH_NEBULA_EDITOR +struct MaterialBindlessBufferBinding +{ + char* buffer = nullptr; + bool* dirtyFlag = nullptr; +}; +#endif /// Create material MaterialId CreateMaterial(const MaterialTemplates::Entry* entry); @@ -54,6 +68,7 @@ void MaterialSetBufferBinding(const MaterialId id, IndexT index); /// Get material GPU buffer binding IndexT MaterialGetBufferBinding(const MaterialId id); + /// Add texture to LOD update void MaterialAddLODTexture(const MaterialId mat, const Resources::ResourceId tex); /// Update LOD for material @@ -70,6 +85,10 @@ const Materials::BatchIndex MaterialGetBatchIndex(const MaterialId mat, const Ma uint64_t MaterialGetSortCode(const MaterialId mat); #ifdef WITH_NEBULA_EDITOR +/// Set pointer to material buffer +void MaterialBindlessForEditor(const MaterialId mat, char* buf, bool* dirtyFlag); +/// Get buffer pointer +const MaterialBindlessBufferBinding& MaterialGetBindlessForEditor(const MaterialId mat); /// Get the pointer to the constants ubyte* MaterialGetConstants(const MaterialId mat); /// Get texture value @@ -112,6 +131,7 @@ enum Material_Template #ifdef WITH_NEBULA_EDITOR , Material_TextureValues + , Material_BufferPointer #endif }; @@ -129,6 +149,7 @@ typedef Ids::IdAllocator< const MaterialTemplates::Entry* // template #ifdef WITH_NEBULA_EDITOR , Util::Array + , MaterialBindlessBufferBinding #endif > MaterialAllocator; extern MaterialAllocator materialAllocator; diff --git a/code/render/materials/materialloader.cc b/code/render/materials/materialloader.cc index b8a050f36..0f58dc0b6 100644 --- a/code/render/materials/materialloader.cc +++ b/code/render/materials/materialloader.cc @@ -20,8 +20,9 @@ struct MaterialBuffer { Ids::IdGenerationPool pool; CoreGraphics::BufferCreateInfo hostBufferCreateInfo, deviceBufferCreateInfo; - INTERFACE_TYPE* hostBufferData; + char* hostBufferData; CoreGraphics::BufferId hostBuffer, deviceBuffer; + uint64 deviceAddress; Util::PinnedArray<0xFFFF, INTERFACE_TYPE> cpuBuffer; bool dirty; @@ -68,8 +69,10 @@ struct MaterialBuffer // Create new buffers this->hostBuffer = CoreGraphics::CreateBuffer(this->hostBufferCreateInfo); - this->hostBufferData = (INTERFACE_TYPE*)CoreGraphics::BufferMap(this->hostBuffer); + this->hostBufferData = (char*)CoreGraphics::BufferMap(this->hostBuffer); this->deviceBuffer = CoreGraphics::CreateBuffer(this->deviceBufferCreateInfo); + this->deviceAddress = CoreGraphics::BufferGetDeviceAddress(this->deviceBuffer); + this->dirty = true; } } return ret; @@ -85,48 +88,13 @@ struct MaterialBuffer /// Flush void Flush(const CoreGraphics::CmdBufferId id, const CoreGraphics::QueueType queue) { - // Copy from cpu buffer to host buffer - if (this->dirty) - { - CoreGraphics::PipelineStage sourceStage = queue == CoreGraphics::GraphicsQueueType ? CoreGraphics::PipelineStage::AllShadersRead : CoreGraphics::PipelineStage::ComputeShaderRead; - Memory::CopyElements(this->cpuBuffer.ConstBegin(), this->hostBufferData, this->cpuBuffer.Size()); - - CoreGraphics::BarrierPush( - id - , CoreGraphics::PipelineStage::HostWrite - , CoreGraphics::PipelineStage::TransferRead - , CoreGraphics::BarrierDomain::Global - , { - CoreGraphics::BufferBarrierInfo - { - .buf = this->hostBuffer, - .subres = CoreGraphics::BufferSubresourceInfo{} - } - } - ); - CoreGraphics::BarrierPush( - id - , sourceStage - , CoreGraphics::PipelineStage::TransferWrite - , CoreGraphics::BarrierDomain::Global - , { - CoreGraphics::BufferBarrierInfo - { - .buf = this->deviceBuffer, - .subres = CoreGraphics::BufferSubresourceInfo{} - } - } - ); + // Copy to host mapped buffer + Memory::Copy(this->cpuBuffer.ConstBegin(), this->hostBufferData, sizeof(INTERFACE_TYPE) * this->cpuBuffer.Size()); - // Now copy to GPU - CoreGraphics::BufferCopy copy; - copy.offset = 0; - CoreGraphics::CmdCopy(id, this->hostBuffer, { copy }, this->deviceBuffer, { copy }, sizeof(INTERFACE_TYPE) * this->cpuBuffer.Size()); - - CoreGraphics::BarrierPop(id); - CoreGraphics::BarrierPop(id); - this->dirty = false; - } + // Copy to device buffer + CoreGraphics::BufferCopy copy; + copy.offset = 0; + CoreGraphics::CmdCopy(id, this->hostBuffer, { copy }, this->deviceBuffer, { copy }, sizeof(INTERFACE_TYPE) * this->cpuBuffer.Size()); } }; @@ -170,7 +138,7 @@ struct int bits; } dirtySet; -} state; +} materialLoaderState; __ImplementClass(Materials::MaterialLoader, 'MALO', Resources::ResourceLoader); @@ -187,24 +155,24 @@ LoadTexture(const Ptr& reader, CoreGraphics::TextureId def, cons { CoreGraphics::TextureIdLock _0(rid); handle = CoreGraphics::TextureGetBindlessHandle(rid); - state.dirtySet.bits = 0x3; + materialLoaderState.dirtySet.bits = BindlessBufferDirtyBits::All; dirtyFlag = true; }, [&handle, &dirtyFlag](Resources::ResourceId rid) mutable { CoreGraphics::TextureIdLock _0(rid); handle = CoreGraphics::TextureGetBindlessHandle(rid); - state.dirtySet.bits = 0x3; + materialLoaderState.dirtySet.bits = BindlessBufferDirtyBits::All; dirtyFlag = true; }); handle = CoreGraphics::TextureGetBindlessHandle(tmp); + reader->SetToParent(); } else { CoreGraphics::TextureIdLock _0(def); handle = CoreGraphics::TextureGetBindlessHandle(def); } - reader->SetToParent(); } //------------------------------------------------------------------------------ @@ -217,12 +185,12 @@ LoadVec4(const Ptr& reader, const char* name, float value[4], co { Math::vec4 val = reader->GetVec4("value"); val.store(value); + reader->SetToParent(); } else { def.store(value); } - reader->SetToParent(); } //------------------------------------------------------------------------------ @@ -235,12 +203,12 @@ LoadVec3(const Ptr& reader, const char* name, float value[3], co { Math::vec4 val = reader->GetVec4("value"); val.store3(value); + reader->SetToParent(); } else { def.store(value); } - reader->SetToParent(); } //------------------------------------------------------------------------------ @@ -252,18 +220,18 @@ LoadFloat(const Ptr& reader, const char* name, float& value, con if (reader->SetToFirstChild(name)) { value = reader->GetFloat("value"); + reader->SetToParent(); } else { value = def; } - reader->SetToParent(); } //------------------------------------------------------------------------------ /** */ -void +void MaterialLoader::Setup() { this->placeholderResourceName = "syssur:placeholder.sur"; @@ -279,22 +247,50 @@ MaterialLoader::Setup() materialBindingInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; materialBindingInfo.usageFlags = CoreGraphics::BufferUsageFlag::ReadWriteBuffer; materialBindingInfo.queueSupport = CoreGraphics::GraphicsQueueSupport; - state.materialBindingBuffer = CoreGraphics::BufferWithStaging(materialBindingInfo); + materialLoaderState.materialBindingBuffer = CoreGraphics::BufferWithStaging(materialBindingInfo); #define ALLOC_MATERIAL(x) \ - Ids::Id32 id = state.x##s.Alloc();\ - state.bindings.x##s = CoreGraphics::BufferGetDeviceAddress(state.x##s.deviceBuffer);\ - MaterialInterfaces::x& material = state.x##s.Get(id);\ - state.x##s.dirty = true; + Ids::Id32 id = materialLoaderState.x##s.Alloc();\ + if (materialLoaderState.bindings.x##s != materialLoaderState.x##s.deviceAddress)\ + {\ + materialLoaderState.bindings.x##s = materialLoaderState.x##s.deviceAddress;\ + materialLoaderState.dirtySet.bits = 0x3;\ + }\ + MaterialInterfaces::x& material = materialLoaderState.x##s.Get(id);\ + materialLoaderState.x##s.dirty = true;\ + +#if WITH_NEBULA_EDITOR + #define ALLOC_AND_BIND_MATERIAL(x) \ + Ids::Id32 id = materialLoaderState.x##s.Alloc();\ + if (materialLoaderState.bindings.x##s != materialLoaderState.x##s.deviceAddress)\ + {\ + materialLoaderState.bindings.x##s = materialLoaderState.x##s.deviceAddress;\ + materialLoaderState.dirtySet.bits = 0x3;\ + }\ + MaterialInterfaces::x& material = materialLoaderState.x##s.Get(id);\ + materialLoaderState.x##s.dirty = true;\ + Materials::MaterialSetBufferBinding(mat, id);\ + Materials::MaterialBindlessForEditor(mat, (char*)&material, &materialLoaderState.x##s.dirty); +#else + #define ALLOC_AND_BIND_MATERIAL(x, editor) \ + Ids::Id32 id = materialLoaderState.x##s.Alloc();\ + if (materialLoaderState.bindings.x##s != materialLoaderState.x##s.deviceAddress)\ + {\ + materialLoaderState.bindings.x##s = materialLoaderState.x##s.deviceAddress;\ + materialLoaderState.dirtySet.bits = 0x3;\ + }\ + MaterialInterfaces::x& material = materialLoaderState.x##s.Get(id);\ + materialLoaderState.x##s.dirty = true;\ + Materials::MaterialSetBufferBinding(mat, id); +#endif auto gltfLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(GLTFMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "baseColorTexture", tag.Value(), material.baseColorTexture, state.GLTFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Green2D, "normalTexture", tag.Value(), material.normalTexture, state.GLTFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Black2D, "metallicRoughnessTexture", tag.Value(), material.metallicRoughnessTexture, state.GLTFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Black2D, "emissiveTexture", tag.Value(), material.emissiveTexture, state.GLTFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Black2D, "occlusionTexture", tag.Value(), material.occlusionTexture, state.GLTFMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(GLTFMaterial); + LoadTexture(reader, CoreGraphics::White2D, "baseColorTexture", tag.Value(), material.baseColorTexture, materialLoaderState.GLTFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Green2D, "normalTexture", tag.Value(), material.normalTexture, materialLoaderState.GLTFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Black2D, "metallicRoughnessTexture", tag.Value(), material.metallicRoughnessTexture, materialLoaderState.GLTFMaterials.dirty); + LoadTexture(reader, CoreGraphics::White2D, "emissiveTexture", tag.Value(), material.emissiveTexture, materialLoaderState.GLTFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Black2D, "occlusionTexture", tag.Value(), material.occlusionTexture, materialLoaderState.GLTFMaterials.dirty); LoadVec4(reader, "baseColorFactor", material.baseColorFactor, Math::vec4(1)); LoadVec4(reader, "emissiveFactor", material.emissiveFactor, Math::vec4(1)); LoadFloat(reader, "metallicFactor", material.metallicFactor, 1); @@ -305,11 +301,10 @@ MaterialLoader::Setup() LoaderMap.Add(MaterialTemplates::MaterialProperties::GLTF, gltfLoader); auto brdfLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(BRDFMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, state.BRDFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Black2D, "ParameterMap", tag.Value(), material.ParameterMap, state.BRDFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Green2D, "NormalMap", tag.Value(), material.NormalMap, state.BRDFMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(BRDFMaterial); + LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, materialLoaderState.BRDFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Black2D, "ParameterMap", tag.Value(), material.ParameterMap, materialLoaderState.BRDFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Green2D, "NormalMap", tag.Value(), material.NormalMap, materialLoaderState.BRDFMaterials.dirty); LoadVec4(reader, "MatAlbedoIntensity", material.MatAlbedoIntensity, Math::vec4(1)); LoadVec4(reader, "MatSpecularIntensity", material.MatSpecularIntensity, Math::vec4(1)); LoadFloat(reader, "MatRoughnessIntensity", material.MatRoughnessIntensity, 1); @@ -320,11 +315,10 @@ MaterialLoader::Setup() LoaderMap.Add(MaterialTemplates::MaterialProperties::BRDF, brdfLoader); auto bsdfLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(BSDFMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, state.BSDFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Black2D, "ParameterMap", tag.Value(), material.ParameterMap, state.BSDFMaterials.dirty); - LoadTexture(reader, CoreGraphics::Green2D, "NormalMap", tag.Value(), material.NormalMap, state.BSDFMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(BSDFMaterial); + LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, materialLoaderState.BSDFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Black2D, "ParameterMap", tag.Value(), material.ParameterMap, materialLoaderState.BSDFMaterials.dirty); + LoadTexture(reader, CoreGraphics::Green2D, "NormalMap", tag.Value(), material.NormalMap, materialLoaderState.BSDFMaterials.dirty); LoadVec4(reader, "MatAlbedoIntensity", material.MatAlbedoIntensity, Math::vec4(1)); LoadVec4(reader, "MatSpecularIntensity", material.MatSpecularIntensity, Math::vec4(1)); LoadFloat(reader, "MatRoughnessIntensity", material.MatRoughnessIntensity, 1); @@ -333,30 +327,27 @@ MaterialLoader::Setup() LoadFloat(reader, "AlphaBlendFactor", material.AlphaBlendFactor, 0); LoadFloat(reader, "Transmission", material.Transmission, 0); }; - LoaderMap.Add(MaterialTemplates::MaterialProperties::BRDF, brdfLoader); + LoaderMap.Add(MaterialTemplates::MaterialProperties::BSDF, bsdfLoader); auto unlitLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(UnlitMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, state.UnlitMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(UnlitMaterial); + LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, materialLoaderState.UnlitMaterials.dirty); }; LoaderMap.Add(MaterialTemplates::MaterialProperties::Unlit, unlitLoader); auto blendAddLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(BlendAddMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, state.BlendAddMaterials.dirty); - LoadTexture(reader, CoreGraphics::White2D, "Layer2", tag.Value(), material.Layer2, state.BlendAddMaterials.dirty); - LoadTexture(reader, CoreGraphics::White2D, "Layer3", tag.Value(), material.Layer3, state.BlendAddMaterials.dirty); - LoadTexture(reader, CoreGraphics::White2D, "Layer4", tag.Value(), material.Layer4, state.BlendAddMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(BlendAddMaterial); + LoadTexture(reader, CoreGraphics::White2D, "AlbedoMap", tag.Value(), material.AlbedoMap, materialLoaderState.BlendAddMaterials.dirty); + LoadTexture(reader, CoreGraphics::White2D, "Layer2", tag.Value(), material.Layer2, materialLoaderState.BlendAddMaterials.dirty); + LoadTexture(reader, CoreGraphics::White2D, "Layer3", tag.Value(), material.Layer3, materialLoaderState.BlendAddMaterials.dirty); + LoadTexture(reader, CoreGraphics::White2D, "Layer4", tag.Value(), material.Layer4, materialLoaderState.BlendAddMaterials.dirty); }; LoaderMap.Add(MaterialTemplates::MaterialProperties::BlendAdd, blendAddLoader); auto skyboxLoader = [](Ptr reader, Materials::MaterialId mat, Util::StringAtom tag) { - ALLOC_MATERIAL(SkyboxMaterial); - Materials::MaterialSetBufferBinding(mat, id); - LoadTexture(reader, CoreGraphics::White2D, "SkyLayer1", tag.Value(), material.SkyLayer1, state.SkyboxMaterials.dirty); - LoadTexture(reader, CoreGraphics::White2D, "SkyLayer2", tag.Value(), material.SkyLayer2, state.SkyboxMaterials.dirty); + ALLOC_AND_BIND_MATERIAL(SkyboxMaterial); + LoadTexture(reader, CoreGraphics::White2D, "SkyLayer1", tag.Value(), material.SkyLayer1, materialLoaderState.SkyboxMaterials.dirty); + LoadTexture(reader, CoreGraphics::White2D, "SkyLayer2", tag.Value(), material.SkyLayer2, materialLoaderState.SkyboxMaterials.dirty); LoadFloat(reader, "SkyBlendFactor", material.SkyBlendFactor, 0); LoadFloat(reader, "SkyRotationFactor", material.SkyRotationFactor, 0); LoadFloat(reader, "Contrast", material.Contrast, 1); @@ -500,10 +491,7 @@ MaterialLoader::InitializeResource(const ResourceLoadJob& job, const PtrSetToNode("Params"); + materialLoaderState.dirtySet.bits = BindlessBufferDirtyBits::All; } // This is the legacy material system loaded with the new surface format @@ -534,33 +522,44 @@ MaterialLoader::InitializeResource(const ResourceLoadJob& job, const Ptr hostBuffers, deviceBuffers; -#define X(x) if (state.x##s.hostBuffer != CoreGraphics::InvalidBufferId) \ - { \ - hostBuffers.Append({.buf = state.x##s.hostBuffer});\ - deviceBuffers.Append({.buf = state.x##s.deviceBuffer});\ - } - MATERIAL_LIST + Util::Array hostBuffers, deviceBuffers; +#define X(x)\ + if (materialLoaderState.x##s.dirty)\ + {\ + if (materialLoaderState.x##s.hostBuffer != CoreGraphics::InvalidBufferId) \ + { \ + hostBuffers.Append({.buf = materialLoaderState.x##s.hostBuffer});\ + deviceBuffers.Append({.buf = materialLoaderState.x##s.deviceBuffer});\ + }\ + anyDirty = true;\ + } + MATERIAL_LIST #undef X - hostBuffers.Append({ .buf = state.materialBindingBuffer.HostBuffer() }); - deviceBuffers.Append({ .buf = state.materialBindingBuffer.DeviceBuffer() }); - CoreGraphics::BufferUpdate(state.materialBindingBuffer.HostBuffer(), state.bindings, 0); + if (materialLoaderState.dirtySet.bits & bits) + { + CoreGraphics::BufferUpdate(materialLoaderState.materialBindingBuffer.HostBuffer(), materialLoaderState.bindings, 0); + hostBuffers.Append({ .buf = materialLoaderState.materialBindingBuffer.HostBuffer() }); + deviceBuffers.Append({ .buf = materialLoaderState.materialBindingBuffer.DeviceBuffer() }); + anyDirty = true; + } + + if (anyDirty) + { CoreGraphics::BarrierPush( - id + cmdBuf , CoreGraphics::PipelineStage::HostWrite , CoreGraphics::PipelineStage::TransferRead , CoreGraphics::BarrierDomain::Global @@ -568,7 +567,7 @@ MaterialLoader::FlushMaterialBuffers(const CoreGraphics::CmdBufferId id, const C ); CoreGraphics::BarrierPush( - id + cmdBuf , sourceStage , CoreGraphics::PipelineStage::TransferWrite , CoreGraphics::BarrierDomain::Global @@ -576,15 +575,22 @@ MaterialLoader::FlushMaterialBuffers(const CoreGraphics::CmdBufferId id, const C ); #define X(x) \ - state.x##s.Flush(id, queue); + if (materialLoaderState.x##s.dirty)\ + {\ + materialLoaderState.x##s.Flush(cmdBuf, queue);\ + materialLoaderState.x##s.dirty = false;\ + } MATERIAL_LIST #undef X - state.materialBindingBuffer.Flush(id, sizeof(MaterialInterfaces::MaterialBindings)); + if (materialLoaderState.dirtySet.bits & bits) + { + materialLoaderState.materialBindingBuffer.Flush(cmdBuf, sizeof(MaterialInterfaces::MaterialBindings)); + materialLoaderState.dirtySet.bits &= ~bits; + } - CoreGraphics::BarrierPop(id); - CoreGraphics::BarrierPop(id); - state.dirtySet.bits &= ~bits; + CoreGraphics::BarrierPop(cmdBuf); + CoreGraphics::BarrierPop(cmdBuf); } } @@ -594,7 +600,7 @@ MaterialLoader::FlushMaterialBuffers(const CoreGraphics::CmdBufferId id, const C CoreGraphics::BufferId MaterialLoader::GetMaterialBindingBuffer() { - return state.materialBindingBuffer.DeviceBuffer(); + return materialLoaderState.materialBindingBuffer.DeviceBuffer(); } //------------------------------------------------------------------------------ @@ -607,7 +613,7 @@ MaterialLoader::GetMaterialBuffer(const MaterialTemplates::MaterialProperties ty { #define X(x) \ case MaterialTemplates::MaterialProperties::x:\ - return state.x##Materials.deviceBuffer;\ + return materialLoaderState.x##Materials.deviceBuffer;\ PROPERTIES_LIST #undef X @@ -625,7 +631,7 @@ MaterialLoader::RegisterTerrainMaterial(const MaterialInterfaces::TerrainMateria material.LowresAlbedoFallback = terrain.LowresAlbedoFallback; material.LowresMaterialFallback = terrain.LowresMaterialFallback; material.LowresNormalFallback = terrain.LowresNormalFallback; - state.dirtySet.bits = 0x3; + materialLoaderState.dirtySet.bits = BindlessBufferDirtyBits::All; return id; } diff --git a/code/render/materials/materialloader.h b/code/render/materials/materialloader.h index 16e057f37..86d93610a 100644 --- a/code/render/materials/materialloader.h +++ b/code/render/materials/materialloader.h @@ -20,6 +20,7 @@ class BXmlReader; namespace Materials { + class MaterialLoader : public Resources::ResourceLoader { __DeclareClass(MaterialLoader); diff --git a/code/render/posteffects/ssaocontext.cc b/code/render/posteffects/ssaocontext.cc index 0c24eaaf5..a22fc928e 100644 --- a/code/render/posteffects/ssaocontext.cc +++ b/code/render/posteffects/ssaocontext.cc @@ -76,7 +76,7 @@ SSAOContext::~SSAOContext() //------------------------------------------------------------------------------ /** */ -void +void SSAOContext::Create() { __CreatePluginContext(); @@ -88,7 +88,7 @@ SSAOContext::Create() //------------------------------------------------------------------------------ /** */ -void +void SSAOContext::Discard() { CoreGraphics::DestroyTexture(ssaoState.internalTargets[0]); @@ -105,7 +105,7 @@ SSAOContext::Discard() //------------------------------------------------------------------------------ /** */ -void +void SSAOContext::Setup() { using namespace CoreGraphics; @@ -241,7 +241,7 @@ SSAOContext::Setup() //------------------------------------------------------------------------------ /** */ -void +void SSAOContext::UpdateViewDependentResources(const Ptr& view, const Graphics::FrameContext& ctx) { // get camera settings @@ -307,21 +307,21 @@ SSAOContext::UpdateViewDependentResources(const Ptr& view, const hbaoBlock.UVToViewA[1] = ssaoState.vars.uvToViewA.y; hbaoBlock.UVToViewB[0] = ssaoState.vars.uvToViewB.x; hbaoBlock.UVToViewB[1] = ssaoState.vars.uvToViewB.y; - uint hbaoOffset = CoreGraphics::SetConstants(hbaoBlock); + uint64 hbaoOffset = CoreGraphics::SetConstants(hbaoBlock); IndexT bufferIndex = CoreGraphics::GetBufferedFrameIndex(); - ResourceTableSetConstantBuffer(ssaoState.hbaoTable[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoCs::Table_Batch::HBAOBlock_SLOT, 0, sizeof(HbaoCs::HBAOBlock), (SizeT)hbaoOffset }); + ResourceTableSetConstantBuffer(ssaoState.hbaoTable[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoCs::Table_Batch::HBAOBlock_SLOT, 0, sizeof(HbaoCs::HBAOBlock), hbaoOffset }); ResourceTableCommitChanges(ssaoState.hbaoTable[bufferIndex]); HbaoblurCs::HBAOBlur blurBlock; blurBlock.BlurFalloff = ssaoState.vars.blurFalloff; blurBlock.BlurDepthThreshold = ssaoState.vars.blurThreshold; blurBlock.PowerExponent = 1.5f; - uint blurOffset = CoreGraphics::SetConstants(blurBlock); + uint64 blurOffset = CoreGraphics::SetConstants(blurBlock); - ResourceTableSetConstantBuffer(ssaoState.blurTableX[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoblurCs::Table_Batch::HBAOBlur_SLOT, 0, sizeof(HbaoblurCs::HBAOBlur), (SizeT)blurOffset }); - ResourceTableSetConstantBuffer(ssaoState.blurTableY[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoblurCs::Table_Batch::HBAOBlur_SLOT, 0, sizeof(HbaoblurCs::HBAOBlur), (SizeT)blurOffset }); + ResourceTableSetConstantBuffer(ssaoState.blurTableX[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoblurCs::Table_Batch::HBAOBlur_SLOT, 0, sizeof(HbaoblurCs::HBAOBlur), blurOffset }); + ResourceTableSetConstantBuffer(ssaoState.blurTableY[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), HbaoblurCs::Table_Batch::HBAOBlur_SLOT, 0, sizeof(HbaoblurCs::HBAOBlur), blurOffset }); ResourceTableCommitChanges(ssaoState.blurTableX[bufferIndex]); ResourceTableCommitChanges(ssaoState.blurTableY[bufferIndex]); } diff --git a/code/render/posteffects/ssrcontext.cc b/code/render/posteffects/ssrcontext.cc index b7e0ac41a..06fcca431 100644 --- a/code/render/posteffects/ssrcontext.cc +++ b/code/render/posteffects/ssrcontext.cc @@ -51,7 +51,7 @@ SSRContext::~SSRContext() //------------------------------------------------------------------------------ /** */ -void +void SSRContext::Create() { __CreatePluginContext(); @@ -96,7 +96,7 @@ SSRContext::Create() //------------------------------------------------------------------------------ /** */ -void +void SSRContext::Discard() { using namespace CoreGraphics; @@ -110,7 +110,7 @@ SSRContext::Discard() //------------------------------------------------------------------------------ /** */ -void +void SSRContext::Setup(const Ptr& script) { SizeT numFrames = CoreGraphics::GetNumBufferedFrames(); @@ -148,7 +148,7 @@ SSRContext::Setup(const Ptr& script) //------------------------------------------------------------------------------ /** */ -void +void SSRContext::UpdateViewDependentResources(const Ptr& view, const Graphics::FrameContext& ctx) { using namespace Graphics; @@ -173,11 +173,11 @@ SSRContext::UpdateViewDependentResources(const Ptr& view, const SsrCs::SSRBlock ssrBlock; viewToTextureSpaceMatrix.store(ssrBlock.ViewToTextureSpace); - uint ssrOffset = CoreGraphics::SetConstants(ssrBlock); + uint64 ssrOffset = CoreGraphics::SetConstants(ssrBlock); IndexT bufferIndex = CoreGraphics::GetBufferedFrameIndex(); - ResourceTableSetConstantBuffer(ssrState.ssrTraceTables[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), SsrCs::Table_Batch::SSRBlock_SLOT, 0, sizeof(SsrCs::SSRBlock), (SizeT)ssrOffset }); + ResourceTableSetConstantBuffer(ssrState.ssrTraceTables[bufferIndex], { CoreGraphics::GetConstantBuffer(bufferIndex), SsrCs::Table_Batch::SSRBlock_SLOT, 0, sizeof(SsrCs::SSRBlock), ssrOffset }); ResourceTableCommitChanges(ssrState.ssrTraceTables[bufferIndex]); } diff --git a/code/render/raytracing/raytracingcontext.cc b/code/render/raytracing/raytracingcontext.cc index 96ba2e616..197790db3 100644 --- a/code/render/raytracing/raytracingcontext.cc +++ b/code/render/raytracing/raytracingcontext.cc @@ -21,6 +21,7 @@ #include "raytracing/shaders/light_grid_cs.h" #include "frame/default.h" +#include "lighting/lightcontext.h" namespace Raytracing { @@ -28,7 +29,7 @@ namespace Raytracing RaytracingContext::RaytracingAllocator RaytracingContext::raytracingContextAllocator; __ImplementContext(RaytracingContext, raytracingContextAllocator); - +static const uint NUM_GRID_CELLS = 64; struct { Threading::CriticalSection blasLock; @@ -40,16 +41,17 @@ struct Memory::RangeAllocator blasInstanceAllocator; bool topLevelNeedsReconstruction, topLevelNeedsBuild, topLevelNeedsUpdate; - Util::HashTable> blasLookup; + Util::HashTable>> blasLookup; CoreGraphics::BufferWithStaging blasInstanceBuffer; - CoreGraphics::ResourceTableSet raytracingTestTables; + CoreGraphics::ResourceTableSet raytracingTables; + CoreGraphics::ResourceTableId raytracingTestOutputTable; CoreGraphics::BufferId geometryBindingBuffer; CoreGraphics::BufferWithStaging objectBindingBuffer; Util::Array objects; - CoreGraphics::BufferId lightGrid; + CoreGraphics::BufferId gridBuffer; CoreGraphics::BufferId lightGridConstants; CoreGraphics::ShaderId lightGridShader; CoreGraphics::ResourceTableSet lightGridResourceTables; @@ -91,13 +93,12 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) { if (!CoreGraphics::RayTracingSupported) return; - + __CreateContext(); #ifndef PUBLIC_BUILD __bundle.OnRenderDebug = RaytracingContext::OnRenderDebug; #endif Graphics::GraphicsServer::Instance()->RegisterGraphicsContext(&__bundle, &__state); - __CreateContext(); auto raygenShader = CoreGraphics::ShaderGet("shd:raytracing/shaders/raytracetest.fxb"); auto raygenProgram = CoreGraphics::ShaderGetProgram(raygenShader, CoreGraphics::ShaderFeatureMask("test")); @@ -128,7 +129,8 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) MaterialPropertyMappings[(uint)MaterialTemplates::MaterialProperties::Skybox] = 0xFFFFFFFF; MaterialPropertyMappings[(uint)MaterialTemplates::MaterialProperties::Terrain] = bindingCounter++; - state.raytracingTestTables = CoreGraphics::ShaderCreateResourceTableSet(raygenShader, NEBULA_BATCH_GROUP, 3); + state.raytracingTables = CoreGraphics::ShaderCreateResourceTableSet(raygenShader, NEBULA_BATCH_GROUP, 3, "Raytracing Descriptors"); + state.raytracingTestOutputTable = CoreGraphics::ShaderCreateResourceTable(raygenShader, NEBULA_SYSTEM_GROUP); state.raytracingBundle = CoreGraphics::CreateRaytracingPipeline(shaderMappings); Raytracetest::Geometry geometryBindings; @@ -148,17 +150,17 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) geometryBindingBufferCreateInfo.dataSize = sizeof(Raytracetest::Geometry); state.geometryBindingBuffer = CoreGraphics::CreateBuffer(geometryBindingBufferCreateInfo); - CoreGraphics::BufferCreateInfo lightGridInfo; - lightGridInfo.name = "RaytracingAABBGrid"; - lightGridInfo.size = 64 * 64 * 64; - lightGridInfo.elementSize = sizeof(LightGridCs::ClusterAABB); - lightGridInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; - lightGridInfo.usageFlags = CoreGraphics::ReadWriteBuffer; - lightGridInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; - state.lightGrid = CoreGraphics::CreateBuffer(lightGridInfo); + CoreGraphics::BufferCreateInfo gridBufferInfo; + gridBufferInfo.name = "RaytracingAABBGrid"; + gridBufferInfo.size = NUM_GRID_CELLS * NUM_GRID_CELLS * NUM_GRID_CELLS; + gridBufferInfo.elementSize = sizeof(LightGridCs::ClusterAABB); + gridBufferInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; + gridBufferInfo.usageFlags = CoreGraphics::ReadWriteBuffer; + gridBufferInfo.queueSupport = CoreGraphics::GraphicsQueueSupport | CoreGraphics::ComputeQueueSupport; + state.gridBuffer = CoreGraphics::CreateBuffer(gridBufferInfo); CoreGraphics::BufferCreateInfo indexListInfo; - indexListInfo.name = "LightIndexListsBuffer"; + indexListInfo.name = "RaytracingLightIndexListsBuffer"; indexListInfo.byteSize = sizeof(LightGridCs::LightIndexLists); indexListInfo.mode = CoreGraphics::BufferAccessMode::DeviceLocal; indexListInfo.usageFlags = CoreGraphics::ReadWriteBuffer; @@ -172,16 +174,16 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) lightGridConstantsInfo.queueSupport = CoreGraphics::ComputeQueueSupport; state.lightGridConstants = CoreGraphics::CreateBuffer(lightGridConstantsInfo); - state.lightGridResourceTables = CoreGraphics::ShaderCreateResourceTableSet(state.lightGridShader, NEBULA_FRAME_GROUP, 3); + state.lightGridResourceTables = CoreGraphics::ShaderCreateResourceTableSet(state.lightGridShader, NEBULA_FRAME_GROUP, 3, "Raytracing Grid Descriptor Set"); for (IndexT i = 0; i < CoreGraphics::GetNumBufferedFrames(); i++) { - ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[i], { state.lightGrid, LightGridCs::Table_Frame::ClusterAABBs_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[i], { state.gridBuffer, LightGridCs::Table_Frame::ClusterAABBs_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[i], { CoreGraphics::GetConstantBuffer(i), LightGridCs::Table_Frame::LightUniforms_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[i], { state.lightGridIndexLists, LightGridCs::Table_Frame::LightIndexLists_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[i], { state.lightGridConstants, LightGridCs::Table_Frame::ClusterUniforms_SLOT, 0, sizeof(LightGridCs::ClusterUniforms), 0 }); ResourceTableCommitChanges(state.lightGridResourceTables.tables[i]); } - // Create buffers for updating blas instances CoreGraphics::BufferCreateInfo bufInfo; bufInfo.name = "BLAS Instance Buffer"; @@ -199,25 +201,26 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) state.objectBindingBuffer = CoreGraphics::BufferWithStaging(objectBindingBufferCreateInfo); FrameScript_default::Bind_RayTracingObjectBindings(state.objectBindingBuffer.DeviceBuffer()); - FrameScript_default::Bind_GridLightList(state.lightGrid); + FrameScript_default::Bind_GridBuffer(state.gridBuffer); FrameScript_default::Bind_GridLightIndexLists(state.lightGridIndexLists); FrameScript_default::RegisterSubgraph_RaytracingLightGridGen_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) { CoreGraphics::CmdSetShaderProgram(cmdBuf, state.lightGridGenProgram); CoreGraphics::CmdSetResourceTable(cmdBuf, state.lightGridResourceTables.tables[bufferIndex], NEBULA_FRAME_GROUP, CoreGraphics::ComputePipeline, nullptr); - CoreGraphics::CmdDispatch(cmdBuf, 64 * 64, 1, 1); + CoreGraphics::CmdDispatch(cmdBuf, NUM_GRID_CELLS * NUM_GRID_CELLS, 1, 1); }, { - { FrameScript_default::BufferIndex::GridLightList, CoreGraphics::PipelineStage::ComputeShaderWrite } + { FrameScript_default::BufferIndex::GridBuffer, CoreGraphics::PipelineStage::ComputeShaderWrite } }); FrameScript_default::RegisterSubgraph_RaytracingLightGridCull_Compute([](const CoreGraphics::CmdBufferId cmdBuf, const Math::rectangle& viewport, const IndexT frame, const IndexT bufferIndex) { CoreGraphics::CmdSetShaderProgram(cmdBuf, state.lightGridCullProgram); CoreGraphics::CmdSetResourceTable(cmdBuf, state.lightGridResourceTables.tables[bufferIndex], NEBULA_FRAME_GROUP, CoreGraphics::ComputePipeline, nullptr); - CoreGraphics::CmdDispatch(cmdBuf, 64 * 64, 1, 1); + CoreGraphics::CmdDispatch(cmdBuf, NUM_GRID_CELLS*NUM_GRID_CELLS, 1, 1); }, { - { FrameScript_default::BufferIndex::GridLightList, CoreGraphics::PipelineStage::ComputeShaderRead } + { FrameScript_default::BufferIndex::GridBuffer, CoreGraphics::PipelineStage::ComputeShaderRead } + , { FrameScript_default::BufferIndex::LightList, CoreGraphics::PipelineStage::ComputeShaderRead } , { FrameScript_default::BufferIndex::GridLightIndexLists, CoreGraphics::PipelineStage::ComputeShaderWrite } }); @@ -341,19 +344,23 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) { if (state.toplevelAccelerationStructure != CoreGraphics::InvalidTlasId) { + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::AllShadersRead, CoreGraphics::PipelineStage::RayTracingShaderWrite, CoreGraphics::BarrierDomain::Global, { CoreGraphics::TextureBarrierInfo{.tex = FrameScript_default::Texture_RayTracingTestOutput(), .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() }}, nullptr, nullptr); CoreGraphics::CmdSetRayTracingPipeline(cmdBuf, state.raytracingBundle.pipeline); - CoreGraphics::CmdSetResourceTable(cmdBuf, state.raytracingTestTables.tables[bufferIndex], NEBULA_BATCH_GROUP, CoreGraphics::RayTracingPipeline, {}); + CoreGraphics::CmdSetResourceTable(cmdBuf, state.raytracingTestOutputTable, NEBULA_SYSTEM_GROUP, CoreGraphics::RayTracingPipeline, nullptr); + CoreGraphics::CmdSetResourceTable(cmdBuf, state.raytracingTables.tables[bufferIndex], NEBULA_BATCH_GROUP, CoreGraphics::RayTracingPipeline, nullptr); CoreGraphics::CmdSetResourceTable(cmdBuf, state.lightGridResourceTables.tables[bufferIndex], NEBULA_FRAME_GROUP, CoreGraphics::RayTracingPipeline, nullptr); CoreGraphics::CmdRaysDispatch(cmdBuf, state.raytracingBundle.table, 640, 480, 1); + CoreGraphics::CmdBarrier(cmdBuf, CoreGraphics::PipelineStage::RayTracingShaderWrite, CoreGraphics::PipelineStage::AllShadersRead, CoreGraphics::BarrierDomain::Global, { CoreGraphics::TextureBarrierInfo{.tex = FrameScript_default::Texture_RayTracingTestOutput(), .subres = CoreGraphics::TextureSubresourceInfo::ColorNoMipNoLayer() }}, nullptr, nullptr); } }, { { FrameScript_default::BufferIndex::GridLightIndexLists, CoreGraphics::PipelineStage::RayTracingShaderRead } + , { FrameScript_default::BufferIndex::GridBuffer, CoreGraphics::PipelineStage::RayTracingShaderRead } , { FrameScript_default::BufferIndex::RayTracingObjectBindings, CoreGraphics::PipelineStage::RayTracingShaderRead } - } ); + }); state.maxAllowedInstances = settings.maxNumAllowedInstances; - + state.topLevelNeedsReconstruction = true; state.blasInstanceAllocator = Memory::RangeAllocator(0xFFFFF, settings.maxNumAllowedInstances); } @@ -363,7 +370,7 @@ RaytracingContext::Create(const RaytracingSetupSettings& settings) void RaytracingContext::Discard() { - state.raytracingTestTables.Discard(); + state.raytracingTables.Discard(); state.lightGridResourceTables.Discard(); } @@ -389,11 +396,11 @@ RaytracingContext::SetupModel(const Graphics::GraphicsEntityId id, CoreGraphics: raytracingContextAllocator.Set(contextId.id, numObjects); raytracingContextAllocator.Set(contextId.id, UpdateType::Dynamic); - IndexT counter = 0; + IndexT instanceCounter = 0; for (IndexT i = nodes.begin; i < nodes.end; i++) { Models::PrimitiveNode* pNode = static_cast(Models::ModelContext::NodeInstances.renderable.nodes[i]); - Resources::CreateResourceListener(pNode->GetMeshResource(), [flags, mask, offset = alloc.offset, counter, i, pNode](Resources::ResourceId id) + Resources::CreateResourceListener(pNode->GetMeshResource(), [flags, mask, offset = alloc.offset, instanceCounter, i, pNode](Resources::ResourceId id) { Threading::CriticalScope _s(&state.blasLock); CoreGraphics::MeshResourceId meshRes = id; @@ -408,29 +415,36 @@ RaytracingContext::SetupModel(const Graphics::GraphicsEntityId id, CoreGraphics: IndexT blasIndex = state.blasLookup.FindIndex(mesh); if (blasIndex == InvalidIndex) { + // Reset primitive group counter const CoreGraphics::VertexLayoutId layout = CoreGraphics::MeshGetVertexLayout(mesh); auto& comps = CoreGraphics::VertexLayoutGetComponents(layout); - CoreGraphics::BlasCreateInfo createInfo; - createInfo.vbo = CoreGraphics::GetVertexBuffer(); - createInfo.ibo = CoreGraphics::GetIndexBuffer(); - createInfo.indexType = CoreGraphics::MeshGetIndexType(mesh); - createInfo.positionsFormat = comps[0].GetFormat(); - createInfo.stride = CoreGraphics::VertexLayoutGetStreamSize(layout, 0); - createInfo.vertexOffset = CoreGraphics::MeshGetVertexOffset(mesh, 0); - createInfo.indexOffset = CoreGraphics::MeshGetIndexOffset(mesh); - createInfo.primitiveGroups = CoreGraphics::MeshGetPrimitiveGroups(mesh); - createInfo.flags = CoreGraphics::AccelerationStructureBuildFlags::FastTrace; - CoreGraphics::BlasId blas = CoreGraphics::CreateBlas(createInfo); - state.blases.Append(blas); - blasIndex = state.blasLookup.Add(mesh, Util::MakeTuple(1, blas)); - - CoreGraphics::BlasIdRelease(blas); - state.blasesToRebuild.Append(blas); + const Util::Array& primGroups = CoreGraphics::MeshGetPrimitiveGroups(mesh); + Util::Array blasesCreated; + blasesCreated.Reserve(primGroups.Size()); + for (auto& group : primGroups) + { + CoreGraphics::BlasCreateInfo createInfo; + createInfo.vbo = CoreGraphics::MeshGetVertexBuffer(mesh, 0); + createInfo.ibo = CoreGraphics::MeshGetIndexBuffer(mesh); + createInfo.indexType = CoreGraphics::MeshGetIndexType(mesh); + createInfo.positionsFormat = comps[0].GetFormat(); + createInfo.stride = CoreGraphics::VertexLayoutGetStreamSize(layout, 0); + createInfo.vertexOffset = CoreGraphics::MeshGetVertexOffset(mesh, 0); + createInfo.indexOffset = CoreGraphics::MeshGetIndexOffset(mesh); + createInfo.primGroup = group; + createInfo.flags = CoreGraphics::AccelerationStructureBuildFlags::FastTrace; + CoreGraphics::BlasId blas = CoreGraphics::CreateBlas(createInfo); + blasesCreated.Append(blas); + state.blases.Append(blas); + CoreGraphics::BlasIdRelease(blas); + state.blasesToRebuild.Append(blas); + } + blasIndex = state.blasLookup.Add(mesh, Util::MakeTuple(1, blasesCreated)); } // Increment ref count - auto& [refCount, blas] = state.blasLookup.ValueAtIndex(mesh, blasIndex); + auto& [refCount, blases] = state.blasLookup.ValueAtIndex(mesh, blasIndex); refCount++; Raytracetest::Object constants; @@ -440,32 +454,44 @@ RaytracingContext::SetupModel(const Graphics::GraphicsEntityId id, CoreGraphics: CoreGraphics::BufferIdLock _2(CoreGraphics::GetIndexBuffer()); // Because the smallest machine unit is 4 bytes, the offset must be in integers, not in bytes - CoreGraphics::PrimitiveGroup group = CoreGraphics::MeshGetPrimitiveGroup(mesh, counter); - constants.Use16BitIndex = CoreGraphics::MeshGetIndexType(mesh) == CoreGraphics::IndexType::Index16 ? 1 : 0; - constants.PositionsPtr = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + CoreGraphics::MeshGetVertexOffset(mesh, 0); - constants.AttrPtr = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + CoreGraphics::MeshGetVertexOffset(mesh, 1); - constants.IndexPtr = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetIndexBuffer()) + CoreGraphics::MeshGetIndexOffset(mesh); + CoreGraphics::IndexType::Code indexType = CoreGraphics::MeshGetIndexType(mesh); + uint positionsStride = (uint)CoreGraphics::VertexLayoutGetStreamSize(CoreGraphics::MeshGetVertexLayout(mesh), 0); + uint attributeStride = (uint)CoreGraphics::VertexLayoutGetStreamSize(CoreGraphics::MeshGetVertexLayout(mesh), 1); + CoreGraphics::PrimitiveGroup group = CoreGraphics::MeshGetPrimitiveGroup(mesh, pNode->GetPrimitiveGroupIndex()); + constants.Use16BitIndex = indexType == CoreGraphics::IndexType::Index16 ? 1 : 0; + CoreGraphics::DeviceAddress positionsAddress = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + CoreGraphics::MeshGetVertexOffset(mesh, 0); + memcpy(constants.PositionsPtr, &positionsAddress, sizeof(CoreGraphics::DeviceAddress)); + CoreGraphics::DeviceAddress attributeAddress = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + CoreGraphics::MeshGetVertexOffset(mesh, 1); + memcpy(constants.AttrPtr, &attributeAddress, sizeof(CoreGraphics::DeviceAddress)); + CoreGraphics::DeviceAddress indexAddress = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetIndexBuffer()) + CoreGraphics::MeshGetIndexOffset(mesh); + memcpy(constants.IndexPtr, &indexAddress, sizeof(CoreGraphics::DeviceAddress)); + constants.AttributeStride = attributeStride; constants.VertexLayout = (uint)temp->vertexLayout; - state.objects[offset + counter] = constants; + constants.BaseIndexOffset = (CoreGraphics::DeviceAddress)group.GetBaseIndex() * CoreGraphics::IndexType::SizeOf(indexType); + constants.BaseVertexPositionOffset = (CoreGraphics::DeviceAddress)group.GetBaseVertex() * positionsStride; + constants.BaseVertexAttributeOffset = (CoreGraphics::DeviceAddress)group.GetBaseVertex() * attributeStride; + + uint instanceIndex = offset + instanceCounter; + state.objects[instanceIndex] = constants; // Setup instance - CoreGraphics::BlasInstanceCreateInfo createInfo; - createInfo.flags = flags; - createInfo.mask = mask; - createInfo.shaderOffset = MaterialPropertyMappings[(uint)temp->properties]; - createInfo.instanceIndex = offset; - createInfo.blas = blas; - createInfo.transform = Models::ModelContext::NodeInstances.transformable.nodeTransforms[Models::ModelContext::NodeInstances.renderable.nodeTransformIndex[i]]; + CoreGraphics::BlasInstanceCreateInfo createIntInfo; + createIntInfo.flags = flags; + createIntInfo.mask = mask; + createIntInfo.shaderOffset = MaterialPropertyMappings[(uint)temp->properties]; + createIntInfo.instanceIndex = instanceIndex; + createIntInfo.blas = blases[pNode->GetPrimitiveGroupIndex()]; + createIntInfo.transform = Models::ModelContext::NodeInstances.transformable.nodeTransforms[Models::ModelContext::NodeInstances.renderable.nodeTransformIndex[i]]; // Disable instance if the vertex layout isn't supported - CoreGraphics::BlasIdLock _0(createInfo.blas); - state.blasInstances[offset + counter] = CoreGraphics::CreateBlasInstance(createInfo); - state.blasInstanceMeshes[offset + counter] = mesh; + CoreGraphics::BlasIdLock _0(createIntInfo.blas); + state.blasInstances[instanceIndex] = CoreGraphics::CreateBlasInstance(createIntInfo); + state.blasInstanceMeshes[instanceIndex] = mesh; state.numRegisteredInstances++; state.topLevelNeedsReconstruction = true; }); - counter++; + instanceCounter++; } state.topLevelNeedsReconstruction = true; } @@ -489,6 +515,9 @@ void RaytracingContext::SetupMesh( , const CoreGraphics::VertexLayoutType vertexLayout ) { + if (!CoreGraphics::RayTracingSupported) + return; + Graphics::ContextEntityId contextId = GetContextId(id); state.blasLock.Enter(); @@ -517,7 +546,7 @@ void RaytracingContext::SetupMesh( createInfo.vertexOffset = vertices.offset + patchCounter * patchVertexStride; createInfo.indexOffset = indices.offset; createInfo.flags = CoreGraphics::AccelerationStructureBuildFlags::FastTrace; - createInfo.primitiveGroups = { patchPrimGroup }; + createInfo.primGroup = patchPrimGroup; CoreGraphics::BlasId blas = CoreGraphics::CreateBlas(createInfo); blases[patchCounter] = blas; state.blasesToRebuild.Append(blas); @@ -543,8 +572,14 @@ void RaytracingContext::SetupMesh( Raytracetest::Object constants; constants.Use16BitIndex = indexType == CoreGraphics::IndexType::Index16 ? 1 : 0; constants.MaterialOffset = materialTableOffset; - constants.IndexPtr = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetIndexBuffer()) + createInfo.indexOffset; - constants.PositionsPtr = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + createInfo.vertexOffset; + CoreGraphics::DeviceAddress indexAddress = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetIndexBuffer()) + createInfo.indexOffset; + memcpy(constants.IndexPtr, &indexAddress, sizeof(CoreGraphics::DeviceAddress)); + CoreGraphics::DeviceAddress positionsAddress = CoreGraphics::BufferGetDeviceAddress(CoreGraphics::GetVertexBuffer()) + createInfo.vertexOffset; + memcpy(constants.PositionsPtr, &positionsAddress, sizeof(CoreGraphics::DeviceAddress)); + constants.AttributeStride = 0x0; + constants.BaseVertexPositionOffset = 0x0; + constants.BaseVertexAttributeOffset = 0x0; + constants.BaseIndexOffset = 0x0; constants.VertexLayout = (uint)vertexLayout; state.objects[i] = constants; @@ -596,31 +631,31 @@ RaytracingContext::ReconstructTopLevelAcceleration(const Graphics::FrameContext& if (state.toplevelAccelerationStructure != CoreGraphics::InvalidTlasId) { + CoreGraphics::ResourceTableSetRWTexture(state.raytracingTestOutputTable, + CoreGraphics::ResourceTableTexture(FrameScript_default::Texture_RayTracingTestOutput(), Raytracetest::Table_System::RaytracingOutput_SLOT) + ); CoreGraphics::ResourceTableSetAccelerationStructure( - state.raytracingTestTables.tables[ctx.bufferIndex], + state.raytracingTables.tables[ctx.bufferIndex], CoreGraphics::ResourceTableTlas(state.toplevelAccelerationStructure, Raytracetest::Table_Batch::TLAS_SLOT) ); - CoreGraphics::ResourceTableSetRWTexture( - state.raytracingTestTables.tables[ctx.bufferIndex], - CoreGraphics::ResourceTableTexture(FrameScript_default::Texture_RayTracingTestOutput(), Raytracetest::Table_Batch::RaytracingOutput_SLOT) - ); CoreGraphics::ResourceTableSetRWBuffer( - state.raytracingTestTables.tables[ctx.bufferIndex], + state.raytracingTables.tables[ctx.bufferIndex], CoreGraphics::ResourceTableBuffer(state.geometryBindingBuffer, Raytracetest::Table_Batch::Geometry_SLOT) ); CoreGraphics::ResourceTableSetRWBuffer( - state.raytracingTestTables.tables[ctx.bufferIndex], + state.raytracingTables.tables[ctx.bufferIndex], CoreGraphics::ResourceTableBuffer(Materials::MaterialLoader::GetMaterialBindingBuffer(), Raytracetest::Table_Batch::MaterialBindings_SLOT) ); CoreGraphics::ResourceTableSetRWBuffer( - state.raytracingTestTables.tables[ctx.bufferIndex], + state.raytracingTables.tables[ctx.bufferIndex], CoreGraphics::ResourceTableBuffer(state.objectBindingBuffer.DeviceBuffer(), Raytracetest::Table_Batch::ObjectBuffer_SLOT) ); - CoreGraphics::ResourceTableCommitChanges(state.raytracingTestTables.tables[ctx.bufferIndex]); + CoreGraphics::ResourceTableCommitChanges(state.raytracingTables.tables[ctx.bufferIndex]); + CoreGraphics::ResourceTableCommitChanges(state.raytracingTestOutputTable); } } @@ -634,11 +669,12 @@ RaytracingContext::UpdateTransforms(const Graphics::FrameContext& ctx) return; LightGridCs::ClusterUniforms uniformData; - uniformData.NumCells[0] = 64; - uniformData.NumCells[1] = 64; - uniformData.NumCells[2] = 64; + uniformData.NumCells[0] = NUM_GRID_CELLS; + uniformData.NumCells[1] = NUM_GRID_CELLS; + uniformData.NumCells[2] = NUM_GRID_CELLS; + uniformData.BlockSize[0] = NUM_GRID_CELLS; + uniformData.BlockSize[1] = 10.0f; // Size of grid cells CoreGraphics::BufferUpdate(state.lightGridConstants, uniformData); - CoreGraphics::BufferFlush(state.lightGridConstants); const Util::Array& entities = RaytracingContext::__state.entities; @@ -743,13 +779,57 @@ RaytracingContext::UpdateViewResources(const Ptr& view, const Gr if (!CoreGraphics::RayTracingSupported) return; - IndexT tickCbo, viewCbo, shadowCbo; + LightsCluster::LightUniforms uniforms = Lighting::LightContext::GetLightUniforms(); + uniforms.NumLightClusters = NUM_GRID_CELLS*NUM_GRID_CELLS*NUM_GRID_CELLS; + uint64 offset = CoreGraphics::SetConstants(uniforms); + uint64 tickCbo, viewCbo, shadowCbo; Graphics::GetOffsets(tickCbo, viewCbo, shadowCbo); - ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { CoreGraphics::GetConstantBuffer(ctx.bufferIndex), LightGridCs::Table_Frame::ViewConstants_SLOT, 0, sizeof(LightGridCs::ViewConstants), (SizeT)viewCbo }); - ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { CoreGraphics::GetConstantBuffer(ctx.bufferIndex), LightGridCs::Table_Frame::ShadowViewConstants_SLOT, 0, sizeof(LightGridCs::ShadowViewConstants), (SizeT)shadowCbo }); + ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { CoreGraphics::GetConstantBuffer(ctx.bufferIndex), Shared::Table_Frame::ViewConstants_SLOT, 0, sizeof(Shared::ViewConstants), viewCbo }); + ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { CoreGraphics::GetConstantBuffer(ctx.bufferIndex), Shared::Table_Frame::ShadowViewConstants_SLOT, 0, sizeof(Shared::ShadowViewConstants), shadowCbo }); + ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { state.gridBuffer, Shared::Table_Frame::ClusterAABBs_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { state.lightGridIndexLists, Shared::Table_Frame::LightIndexLists_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetRWBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { Lighting::LightContext::GetLightsBuffer(), Shared::Table_Frame::LightLists_SLOT, 0, NEBULA_WHOLE_BUFFER_SIZE, 0 }); + ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { state.lightGridConstants, Shared::Table_Frame::ClusterUniforms_SLOT, 0, sizeof(Shared::ClusterUniforms), 0 }); + ResourceTableSetConstantBuffer(state.lightGridResourceTables.tables[ctx.bufferIndex], { CoreGraphics::GetConstantBuffer(ctx.bufferIndex), Shared::Table_Frame::LightUniforms_SLOT, 0, sizeof(Shared::LightUniforms), offset }); ResourceTableCommitChanges(state.lightGridResourceTables.tables[ctx.bufferIndex]); } +//------------------------------------------------------------------------------ +/** +*/ +CoreGraphics::ResourceTableId +RaytracingContext::GetLightGridResourceTable(IndexT bufferIndex) +{ + return state.lightGridResourceTables.tables[bufferIndex]; +} + +//------------------------------------------------------------------------------ +/** +*/ +CoreGraphics::TlasId +RaytracingContext::GetTLAS() +{ + return state.toplevelAccelerationStructure; +} + +//------------------------------------------------------------------------------ +/** +*/ +CoreGraphics::BufferId +RaytracingContext::GetObjectBindingBuffer() +{ + return state.objectBindingBuffer.DeviceBuffer(); +} + +//------------------------------------------------------------------------------ +/** +*/ +CoreGraphics::ResourceTableId +RaytracingContext::GetRaytracingTable(const IndexT bufferIndex) +{ + return state.raytracingTables.tables[bufferIndex]; +} + //------------------------------------------------------------------------------ /** */ @@ -774,20 +854,27 @@ RaytracingContext::Dealloc(Graphics::ContextEntityId id) SizeT numAllocs = raytracingContextAllocator.Get(id.id); for (IndexT i = range.offset; i < numAllocs; i++) { + CoreGraphics::BlasInstanceIdLock _0(state.blasInstances[i]); CoreGraphics::DestroyBlasInstance(state.blasInstances[i]); CoreGraphics::MeshId mesh = state.blasInstanceMeshes[i]; // Delete a mesh if (mesh != CoreGraphics::InvalidMeshId) { - auto& [refCount, blas] = state.blasLookup.ValueAtIndex(mesh, i); - if (refCount == 1) + IndexT index = state.blasLookup.FindIndex(mesh); + if (index != InvalidIndex) { - CoreGraphics::DestroyBlas(blas); - state.blasLookup.EraseIndex(mesh, i); + auto& [refCount, blases] = state.blasLookup.ValueAtIndex(mesh, index); + if (refCount == 1) + { + for (auto blas : blases) + CoreGraphics::DestroyBlas(blas); + state.blasLookup.EraseIndex(mesh, i); + state.blasInstanceMeshes[i] = CoreGraphics::InvalidMeshId; + } + else + refCount--; } - else - refCount--; } else { @@ -799,7 +886,7 @@ RaytracingContext::Dealloc(Graphics::ContextEntityId id) } state.blasInstances[i] = CoreGraphics::InvalidBlasInstanceId; } - + raytracingContextAllocator.Dealloc(id.id); state.topLevelNeedsReconstruction = true; } diff --git a/code/render/raytracing/raytracingcontext.h b/code/render/raytracing/raytracingcontext.h index 46b804f01..b3be22ec4 100644 --- a/code/render/raytracing/raytracingcontext.h +++ b/code/render/raytracing/raytracingcontext.h @@ -80,6 +80,15 @@ class RaytracingContext : public Graphics::GraphicsContext /// Update view constants static void UpdateViewResources(const Ptr& view, const Graphics::FrameContext& ctx); + /// Get light grid resources + static CoreGraphics::ResourceTableId GetLightGridResourceTable(IndexT bufferIndex); + /// Get TLAS + static CoreGraphics::TlasId GetTLAS(); + /// Get object binding buffer + static CoreGraphics::BufferId GetObjectBindingBuffer(); + /// Get raytracing table + static CoreGraphics::ResourceTableId GetRaytracingTable(const IndexT bufferIndex); + #ifndef PUBLIC_DEBUG /// debug rendering static void OnRenderDebug(uint32_t flags); diff --git a/code/render/raytracing/shaders/brdfhit.fx b/code/render/raytracing/shaders/brdfhit.fx index 2a0f82fb3..ec61e8332 100644 --- a/code/render/raytracing/shaders/brdfhit.fx +++ b/code/render/raytracing/shaders/brdfhit.fx @@ -9,7 +9,7 @@ */ shader void ClosestHit( - [ray_payload] in HitResult Result, + [ray_payload] in LightResponsePayload Result, [hit_attribute] in vec2 baryCoords ) { @@ -18,19 +18,35 @@ ClosestHit( uvec3 indices; vec2 uv; - mat3 tbn; - SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, tbn); - + vec3 normal, tangent; + float sign; + SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, normal, tangent, sign); + + normal = gl_ObjectToWorldEXT * vec4(normal, 0); + tangent = gl_ObjectToWorldEXT * vec4(tangent, 0); + mat3 tbn = TangentSpace(tangent, normal, sign); + BRDFMaterial mat = BRDFMaterials + obj.MaterialOffset; - vec4 normals = sample2DLod(mat.NormalMap, Basic2DSampler, uv, 0); + vec4 normals = sample2DLod(mat.NormalMap, NormalSampler, uv, 0); vec3 tNormal = TangentSpaceNormal(normals.xy, tbn); - - vec4 albedo = sample2DLod(mat.AlbedoMap, Basic2DSampler, uv, 0); + + float facing = dot(normal, gl_WorldRayDirectionEXT); + Result.bits |= facing > 0 ? RAY_BACK_FACE_BIT : 0x0; + + vec4 albedo = sample2DLod(mat.AlbedoMap, Basic2DSampler, uv, 0) * mat.MatAlbedoIntensity; + albedo.a *= mat.AlphaBlendFactor; vec4 material = sample2DLod(mat.ParameterMap, Basic2DSampler, uv, 0); + material[MAT_ROUGHNESS] *= mat.MatRoughnessIntensity; + material[MAT_METALLIC] += mat.MatMetallicIntensity; + + vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT; + vec3 radiance = CalculateLightRT(worldPos, gl_WorldRayOriginEXT.xyz, gl_HitTEXT / gl_RayTmaxEXT, albedo.rgb, material, tNormal) + albedo.xyz * material[MAT_EMISSIVE] * albedo.a; + Result.alpha = albedo.a; Result.albedo = albedo.rgb; - Result.material = material; + Result.radiance = radiance; Result.normal = tNormal; + Result.material = material; Result.depth = gl_HitTEXT; } diff --git a/code/render/raytracing/shaders/bsdfhit.fx b/code/render/raytracing/shaders/bsdfhit.fx index a55a3e131..2c4f754a3 100644 --- a/code/render/raytracing/shaders/bsdfhit.fx +++ b/code/render/raytracing/shaders/bsdfhit.fx @@ -10,7 +10,7 @@ */ shader void ClosestHit( - [ray_payload] in HitResult Result, + [ray_payload] in LightResponsePayload Result, [hit_attribute] in vec2 baryCoords ) { @@ -19,19 +19,32 @@ ClosestHit( uvec3 indices; vec2 uv; - mat3 tbn; - SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, tbn); - + vec3 normal, tangent; + float sign; + SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, normal, tangent, sign); + + normal = gl_ObjectToWorldEXT * vec4(normal, 0); + tangent = gl_ObjectToWorldEXT * vec4(tangent, 0); + mat3 tbn = TangentSpace(tangent, normal, sign); + BSDFMaterial mat = BSDFMaterials + obj.MaterialOffset; - vec4 normals = sample2DLod(mat.NormalMap, Basic2DSampler, uv, 0); + vec4 normals = sample2DLod(mat.NormalMap, NormalSampler, uv, 0); vec3 tNormal = TangentSpaceNormal(normals.xy, tbn); + + float facing = dot(normal, gl_WorldRayDirectionEXT); + Result.bits |= facing > 0 ? RAY_BACK_FACE_BIT : 0x0; vec4 albedo = sample2DLod(mat.AlbedoMap, Basic2DSampler, uv, 0); vec4 material = sample2DLod(mat.ParameterMap, Basic2DSampler, uv, 0); - Result.alpha = albedo.a; + + vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT; + vec3 radiance = CalculateLightRT(worldPos, gl_WorldRayOriginEXT.xyz, gl_HitTEXT / gl_RayTmaxEXT, albedo.rgb, material, tNormal) + albedo.xyz * material[MAT_EMISSIVE] * albedo.a; + Result.albedo = albedo.rgb; - Result.material = material; + Result.alpha = albedo.a; + Result.radiance = radiance; Result.normal = tNormal; + Result.material = material; Result.depth = gl_HitTEXT; } diff --git a/code/render/raytracing/shaders/gltfhit.fx b/code/render/raytracing/shaders/gltfhit.fx index 60473b7bd..996531f76 100644 --- a/code/render/raytracing/shaders/gltfhit.fx +++ b/code/render/raytracing/shaders/gltfhit.fx @@ -10,7 +10,7 @@ */ shader void ClosestHit( - [ray_payload] in HitResult Result, + [ray_payload] in LightResponsePayload Result, [hit_attribute] in vec2 baryCoords ) { @@ -19,17 +19,25 @@ ClosestHit( uvec3 indices; vec2 uv; - mat3 tbn; - SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, tbn); + vec3 normal, tangent; + float sign; + SampleGeometry(obj, gl_PrimitiveID, barycentricCoords, indices, uv, normal, tangent, sign); + + normal = gl_ObjectToWorldEXT * vec4(normal, 0); + tangent = gl_ObjectToWorldEXT * vec4(tangent, 0); + mat3 tbn = TangentSpace(tangent, normal, sign); + + float facing = dot(normal, gl_WorldRayDirectionEXT); + Result.bits |= facing > 0 ? RAY_BACK_FACE_BIT : 0x0; - GLTFMaterial mat = GLTFMaterials + obj.MaterialOffset; + GLTFMaterial mat = GLTFMaterials[obj.MaterialOffset]; /* Tangent space normal transform */ - vec4 normals = sample2DLod(mat.normalTexture, Basic2DSampler, uv, 0); + vec4 normals = sample2DLod(mat.normalTexture, NormalSampler, uv, 0) * mat.normalScale; vec3 tNormal = normalize(TangentSpaceNormal(normals.xy, tbn)); /* Surface properties */ - vec4 albedo = sample2DLod(mat.baseColorTexture, Basic2DSampler, uv, 0); + vec4 albedo = sample2DLod(mat.baseColorTexture, Basic2DSampler, uv, 0) * mat.baseColorFactor; vec4 metallicRoughness = sample2DLod(mat.metallicRoughnessTexture, Basic2DSampler, uv, 0) * vec4(1.0f, mat.roughnessFactor, mat.metallicFactor, 1.0f); vec4 emissive = sample2DLod(mat.emissiveTexture, Basic2DSampler, uv, 0) * mat.emissiveFactor; vec4 occlusion = sample2DLod(mat.occlusionTexture, Basic2DSampler, uv, 0); @@ -39,12 +47,16 @@ ClosestHit( material[MAT_METALLIC] = metallicRoughness.b; material[MAT_ROUGHNESS] = metallicRoughness.g; material[MAT_CAVITY] = Greyscale(occlusion); - material[MAT_EMISSIVE] = 0.0f; + material[MAT_EMISSIVE] = 0; // Emissive isn't used by the light RT + vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT; + vec3 radiance = CalculateLightRT(worldPos, gl_WorldRayOriginEXT.xyz, gl_HitTEXT / gl_RayTmaxEXT, albedo.rgb, material, tNormal) + emissive.xyz * albedo.a; + Result.alpha = albedo.a; Result.albedo = albedo.rgb; - Result.material = material; + Result.radiance = radiance; Result.normal = tNormal; + Result.material = material; Result.depth = gl_HitTEXT; } diff --git a/code/render/raytracing/shaders/light_grid_cs.fx b/code/render/raytracing/shaders/light_grid_cs.fx index ae8b2208b..14b46bcd1 100644 --- a/code/render/raytracing/shaders/light_grid_cs.fx +++ b/code/render/raytracing/shaders/light_grid_cs.fx @@ -64,8 +64,9 @@ void csCull() for (uint i = 0; i < NumAreaLights; i++) { const AreaLight light = AreaLights[i]; - // TODO: Area lights needs to be in world space - if (TestAABBAABB(aabb, light.bboxMin, light.bboxMax)) + vec3 worldSpaceMin = light.center - light.extents; + vec3 worldSpaceMax = light.center + light.extents; + if (TestAABBAABB(aabb, worldSpaceMin, worldSpaceMax)) { AreaLightIndexList[index1D * MAX_LIGHTS_PER_CLUSTER + numLights] = i; numLights++; @@ -92,13 +93,14 @@ void csClusterAABB() if (index1D > NumCells.x * NumCells.y * NumCells.z) return; - vec3 offsetMin = vec3(index3D.x - NumCells.x * 0.5f, index3D.y - NumCells.y * 0.5f, index3D.z - NumCells.z * 0.5f); - vec3 offsetMax = vec3(index3D.x + 1 - NumCells.x * 0.5f, index3D.y + 1 - NumCells.y * 0.5f, index3D.z + 1 - NumCells.z * 0.5f); + vec3 offsetMin = vec3(index3D.x - NumCells.x * 0.5f, index3D.y - NumCells.y * 0.5f, index3D.z - NumCells.z * 0.5f) * BlockSize.y; + vec3 offsetMax = vec3((index3D.x + 1) - NumCells.x * 0.5f, (index3D.y + 1) - NumCells.y * 0.5f, (index3D.z + 1) - NumCells.z * 0.5f) * BlockSize.y; // Calculate AABB using min and max ClusterAABB aabb; - aabb.minPoint = vec4(EyePos.xyz + offsetMin * BlockSize.x, 1); - aabb.maxPoint = vec4(EyePos.xyz + offsetMax * BlockSize.x, 1); + vec3 eye = EyePos.xyz; + aabb.minPoint = vec4(eye.xyz + offsetMin, 1); + aabb.maxPoint = vec4(eye.xyz + offsetMax, 1); vec3 extents = (aabb.maxPoint.xyz - aabb.minPoint.xyz) * 0.5f; float radius = dot(extents, extents); aabb.featureFlags = 0; diff --git a/code/render/raytracing/shaders/raytracetest.fx b/code/render/raytracing/shaders/raytracetest.fx index 820487862..ecca3bb11 100644 --- a/code/render/raytracing/shaders/raytracetest.fx +++ b/code/render/raytracing/shaders/raytracetest.fx @@ -6,13 +6,14 @@ #include #include +group(SYSTEM_GROUP) write rgba16f image2D RaytracingOutput; //------------------------------------------------------------------------------ /** */ shader void Raygen( - [ray_payload] out HitResult Result + [ray_payload] out LightResponsePayload Result ) { const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); @@ -23,33 +24,30 @@ Raygen( vec4 target = InvProjection * vec4(d.x, d.y, 1, 1); vec4 direction = InvView * vec4(normalize(target.xyz), 0); - Result.albedo = vec3(0, 0, 0); + Result.bits = 0x0; + Result.albedo = vec3(0); + Result.material = vec4(0); + Result.radiance = vec3(0); Result.alpha = 0.0f; - Result.material = vec4(0, 0, 0, 0); - Result.normal = vec3(0, 0, 0); + Result.normal = vec3(0); Result.depth = 0; - Result.miss = 0; - // Ray trace against BVH - traceRayEXT(TLAS, gl_RayFlagsNoneEXT, 0xFF, 0, 0, 0, origin.xyz, 0.01f, direction.xyz, 10000.0f, 0); + traceRayEXT(TLAS, gl_RayFlagsCullBackFacingTrianglesEXT, 0xFF, 0, 0, 0, origin.xyz, 0.01f, direction.xyz, 10000.0f, 0); - vec3 F0 = CalculateF0(Result.albedo.rgb, Result.material[MAT_METALLIC], vec3(0.04)); - vec3 WorldSpacePos = origin.xyz + direction.xyz * Result.depth; vec3 light = vec3(0); - if (Result.miss == 1) + if ((Result.bits & RAY_MISS_BIT) != 0) { vec3 dir = normalize(direction.xyz); light += CalculateAtmosphericScattering(dir, GlobalLightDirWorldspace.xyz) * GlobalLightColor.rgb; } else { - light += CalculateLightRT(WorldSpacePos, Result.depth / 10000.0f, Result.albedo.rgb, Result.material, Result.normal); + + //light += (Result.normal + 1.0f) * 0.5f; + light += Result.radiance; } - //light += CalculateGlobalLight(Result.albedo, Result.material, F0, -normalize(target.xyz), Result.normal, WorldSpacePos); - //vec3 dir = normalize(Result.normal); - //vec3 atmo = CalculateAtmosphericScattering(dir, GlobalLightDirWorldspace.xyz) * GlobalLightColor.rgb; - imageStore(RaytracingOutput, ivec2(gl_LaunchIDEXT.xy), vec4(light / 10.0f, 0.0f)); + imageStore(RaytracingOutput, ivec2(gl_LaunchIDEXT.xy), vec4(light, 0.0f)); } //------------------------------------------------------------------------------ @@ -60,7 +58,7 @@ Miss( [ray_payload] in HitResult Result ) { - Result.miss = 1; + Result.bits |= RAY_MISS_BIT; } //------------------------------------------------------------------------------ diff --git a/code/render/raytracing/shaders/terrainhit.fx b/code/render/raytracing/shaders/terrainhit.fx index 81b39886d..ec66b2fee 100644 --- a/code/render/raytracing/shaders/terrainhit.fx +++ b/code/render/raytracing/shaders/terrainhit.fx @@ -9,7 +9,7 @@ */ shader void BoxHit( - [ray_payload] in HitResult Result, + [ray_payload] in LightResponsePayload Result, [hit_attribute] in vec2 baryCoords ) { @@ -22,15 +22,20 @@ BoxHit( TerrainMaterial mat = TerrainMaterials[obj.MaterialOffset]; SampleTerrain(obj, gl_PrimitiveID, barycentricCoords, indices, uv, tbn); - vec4 normals = sample2DLod(mat.LowresNormalFallback, Basic2DSampler, uv, 0); + vec4 normals = sample2DLod(mat.LowresNormalFallback, NormalSampler, uv, 0); vec3 tNormal = TangentSpaceNormal(normals.xy, tbn); vec4 albedo = sample2DLod(mat.LowresAlbedoFallback, Basic2DSampler, uv, 0); vec4 material = sample2DLod(mat.LowresMaterialFallback, Basic2DSampler, uv, 0); + + vec3 worldPos = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT; + vec3 radiance = CalculateLightRT(worldPos, gl_WorldRayOriginEXT.xyz, gl_HitTEXT / gl_RayTmaxEXT, albedo.rgb, material, tNormal) + albedo.xyz * material[MAT_EMISSIVE] * albedo.a; + Result.alpha = albedo.a; Result.albedo = albedo.rgb; - Result.material = material; + Result.radiance = radiance; Result.normal = tNormal; + Result.material = material; Result.depth = gl_HitTEXT; } diff --git a/code/render/visibility/visibilitycontext.h b/code/render/visibility/visibilitycontext.h index cb00c749e..1949ed2e0 100644 --- a/code/render/visibility/visibilitycontext.h +++ b/code/render/visibility/visibilitycontext.h @@ -187,9 +187,6 @@ class ObservableContext : public Graphics::GraphicsContext friend class ObserverContext; friend class Models::ModelContext; - using AtomTransform = Math::mat4; - using AtomIsActive = bool; - // observable corresponds to a single entity typedef Ids::IdAllocator< Graphics::GraphicsEntityId, // The entity id that this component is bound to diff --git a/code/resource/CMakeLists.txt b/code/resource/CMakeLists.txt index f770ca0bb..955997c51 100644 --- a/code/resource/CMakeLists.txt +++ b/code/resource/CMakeLists.txt @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------- -# Render +# Resources #------------------------------------------------------------------------------- nebula_begin_module(resource) diff --git a/code/resource/resources/resourceserver.h b/code/resource/resources/resourceserver.h index f6ea32e16..5535227d9 100644 --- a/code/resource/resources/resourceserver.h +++ b/code/resource/resources/resourceserver.h @@ -341,7 +341,7 @@ ResourceServer::GetStreamLoader() const IndexT i = this->typeMap.FindIndex(&POOL_TYPE::RTTI); if (i != InvalidIndex) { - return static_cast(this->loaders[i].get()); + return static_cast(this->loaders[this->typeMap.ValueAtIndex(i)].get()); } n_error("No loader registered for this type"); diff --git a/fips-files/converters/surface_updater.py b/fips-files/converters/surface_updater.py new file mode 100644 index 000000000..7c465a944 --- /dev/null +++ b/fips-files/converters/surface_updater.py @@ -0,0 +1,117 @@ +import xml.etree.ElementTree as ET +import argparse +import os +import sys + +def indent_xml(elem, level=0): + """Adds indentation to an XML element and its children for pretty-printing.""" + i = "\n" + " " * level # Four spaces per level + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for child in elem: + indent_xml(child, level + 1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if not elem.tail or not elem.tail.strip(): + elem.tail = i + +def convert_xml(input_file, output_file): + try: + # Normalize file paths for cross-platform compatibility + input_file = os.path.normpath(input_file) + output_file = os.path.normpath(output_file) + + # Parse the input XML file + tree = ET.parse(input_file) + root = tree.getroot() + + # Ensure the root is + if root.tag != "Nebula": + raise ValueError("Expected root tag , but got <{}>".format(root.tag)) + + # Find the tag within + surface_tag = root.find("Surface") + if surface_tag is None: + raise ValueError("Expected tag within , but none was found.") + + # Create a new element + params_tag = ET.Element("Params") + + # Iterate through elements within + for param in surface_tag.findall("Param"): + name = param.get("name") + value = param.get("value") + + if name and value: + # Create a new tag for each name and set the "value" attribute + new_tag = ET.Element(name) + new_tag.set("value", value) # Set the value as an attribute + params_tag.append(new_tag) + + # Remove all existing elements from + for param in surface_tag.findall("Param"): + surface_tag.remove(param) + + # Add the new tag to + surface_tag.append(params_tag) + + # Pretty-print the XML by adding indentation + indent_xml(root) + + # Write the formatted XML to the output file + tree.write(output_file, encoding="utf-8", xml_declaration=True) + + print(f"Converted XML written to {output_file}") + except Exception as e: + print(f"Error processing file {input_file}: {e}", file=sys.stderr) + +def process_directory(input_dir): + """Recursively processes all XML files in a directory structure.""" + for root, _, files in os.walk(input_dir): + for file in files: + if file.endswith(".sur"): + input_file = os.path.join(root, file) + output_file = input_file # Overwrite the original file + print(f"Processing {input_file}...") + convert_xml(input_file, output_file) + +def main(): + # Set up command-line argument parsing + parser = argparse.ArgumentParser(description="Convert XML nodes into with child tags.") + parser.add_argument( + "--file", + help="Path to a single input XML file to process (mutually exclusive with --dir)" + ) + parser.add_argument( + "--dir", + help="Path to a directory containing XML files to process recursively (mutually exclusive with --file)" + ) + + # Parse the arguments + args = parser.parse_args() + + if args.file and args.dir: + print("Error: You must specify either --file or --dir, not both.", file=sys.stderr) + sys.exit(1) + + if args.file: + # Process a single file + input_file = os.path.normpath(args.file) + output_file = input_file # Overwrite the original file + convert_xml(input_file, output_file) + + elif args.dir: + # Process all files in a directory recursively + input_dir = os.path.normpath(args.dir) + process_directory(input_dir) + + else: + print("Error: You must specify either --file or --dir.", file=sys.stderr) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/fips-files/generators/materialtemplatec.py b/fips-files/generators/materialtemplatec.py index ba13de1f3..4ea29d212 100644 --- a/fips-files/generators/materialtemplatec.py +++ b/fips-files/generators/materialtemplatec.py @@ -323,7 +323,7 @@ def FormatShader(self): texCounter += 1 ret = "" - ret += "ptr struct {}Material \n{{\n{}}};\n".format(self.name, contents) + ret += "ptr alignment(16) struct {}Material \n{{\n{}}};\n".format(self.name, contents) ret += textures ret += "\n" ret += "MATERIAL_CB_BINDING constant {}Constants \n{{\n{}}} _{};\n".format(self.name, contents, self.name) diff --git a/fips-files/include.cmake b/fips-files/include.cmake index cbd1fd420..aac4752fc 100644 --- a/fips-files/include.cmake +++ b/fips-files/include.cmake @@ -317,6 +317,7 @@ macro(nebula_material_template_compile) COMMAND ${PYTHON} ${NROOT}/fips-files/generators/materialtemplatec.py ${material_definition_files} ${SHADERC} ${NROOT}/syswork/shaders/vk "${abs_output_folder}" BYPRODUCTS "${abs_output_folder}/${out_header}" "${abs_output_folder}/${out_source}" "${abs_output_folder}/${out_shader}" "${abs_output_folder}/${out_shader_header}" WORKING_DIRECTORY "${NROOT}" + SOURCES ${material_definition_files} DEPENDS ${NROOT}/fips-files/generators/materialtemplatec.py ${material_definition_files} VERBATIM) add_dependencies(render materialtemplates) @@ -353,6 +354,7 @@ macro(nebula_framescript_compile) COMMAND ${PYTHON} ${NROOT}/fips-files/generators/framescriptc.py ${script} "${abs_output_folder}/${out_header}" "${abs_output_folder}/${out_source}" BYPRODUCTS "${abs_output_folder}/${out_header}" "${abs_output_folder}/${out_source}" WORKING_DIRECTORY "${NROOT}" + SOURCES ${script} DEPENDS ${NROOT}/fips-files/generators/framescriptc.py ${script} VERBATIM) add_dependencies(render ${target_name}) diff --git a/fips.yml b/fips.yml index 93a77a52f..7e70ee7bb 100644 --- a/fips.yml +++ b/fips.yml @@ -58,6 +58,7 @@ exports: - code/addons - code/audio - code/input + - code/options - toolkit/editor modules: foundation: code/foundation @@ -68,4 +69,5 @@ exports: addons: code/addons audio: code/audio input: code/input + options: code/options toolkit: toolkit diff --git a/syswork/data/flatbuffer/graphicsfeature/graphicsfeatureschema.fbs b/syswork/data/flatbuffer/graphicsfeature/graphicsfeatureschema.fbs deleted file mode 100644 index b5c335c2d..000000000 --- a/syswork/data/flatbuffer/graphicsfeature/graphicsfeatureschema.fbs +++ /dev/null @@ -1,33 +0,0 @@ -//------------------------------------------------------------------------------ -// Graphics Feature settings for flatbuffer -// -// (C) 2022 Individual contributors, see AUTHORS file -//------------------------------------------------------------------------------ - -include "foundation/math.fbs"; -namespace GraphicsFeature; - -table ClusterSettings -{ - min_z: float = 0.001; - max_z: float = 10000.0; -} - -table HistogramSettings -{ - window_left: float = 0; - window_bottom: float = 0; - window_width: float = 1; - window_height: float = 1; - mip: int = 1; -} - -table SunSettings -{ - cast_shadows: bool; - color: Flat.Vec3 (native_inline); - intensity: float; - ambient: Flat.Vec3 (native_inline); - backlight: Flat.Vec3 (native_inline); - direction: Flat.Vec3 (native_inline); -} \ No newline at end of file diff --git a/syswork/data/flatbuffer/graphicsfeature/vegetationschema.fbs b/syswork/data/flatbuffer/graphicsfeature/vegetationschema.fbs deleted file mode 100644 index 960f73619..000000000 --- a/syswork/data/flatbuffer/graphicsfeature/vegetationschema.fbs +++ /dev/null @@ -1,16 +0,0 @@ -//------------------------------------------------------------------------------ -// Vegetation settings for flatbuffer -// -// (C) 2023 Individual contributors, see AUTHORS file -//------------------------------------------------------------------------------ - -namespace GraphicsFeature; - -table VegetationSetup -{ - use: bool = false; -} - -root_type VegetationSetup; -file_identifier "PVEG"; -file_extension "pveg"; \ No newline at end of file diff --git a/syswork/data/flatbuffer/graphicsfeature/terrainschema.fbs b/syswork/data/flatbuffer/options/levelsettings.fbs similarity index 57% rename from syswork/data/flatbuffer/graphicsfeature/terrainschema.fbs rename to syswork/data/flatbuffer/options/levelsettings.fbs index fae1a2558..27e586eaa 100644 --- a/syswork/data/flatbuffer/graphicsfeature/terrainschema.fbs +++ b/syswork/data/flatbuffer/options/levelsettings.fbs @@ -1,10 +1,13 @@ //------------------------------------------------------------------------------ -// Terrain settings for flatbuffer // -// (C) 2023 Individual contributors, see AUTHORS file +// Project settings format +// +// (C) 2024 Individual contributors, see AUTHORS file +// //------------------------------------------------------------------------------ +include "foundation/math.fbs"; -namespace GraphicsFeature; +namespace App; table TerrainConfig { use: bool; @@ -58,6 +61,45 @@ table TerrainSetup biomes: [TerrainBiomeSettings]; } -root_type TerrainSetup; -file_identifier "PTER"; -file_extension "pter"; \ No newline at end of file +table VegetationSetup +{ + use: bool = false; +} + +table SunSettings +{ + cast_shadows: bool; + color: Flat.Vec3 (native_inline); + intensity: float; + ambient: Flat.Vec3 (native_inline); + backlight: Flat.Vec3 (native_inline); + direction: Flat.Vec3 (native_inline); +} + +table ClusterSettings +{ + min_z: float = 0.001; + max_z: float = 10000.0; +} + +table HistogramSettings +{ + window_left: float = 0; + window_bottom: float = 0; + window_width: float = 1; + window_height: float = 1; + mip: int = 1; +} + +table LevelSettings +{ + terrain_setup: TerrainSetup; + vegetation_setup: VegetationSetup; + sun_settings: SunSettings; + cluster_settings: ClusterSettings; + histogram_settings: HistogramSettings; +} + +root_type LevelSettings; +file_identifier "NLST"; +file_extension "nlst"; \ No newline at end of file diff --git a/syswork/data/flatbuffer/options/projectsettings.fbs b/syswork/data/flatbuffer/options/projectsettings.fbs new file mode 100644 index 000000000..412c95a71 --- /dev/null +++ b/syswork/data/flatbuffer/options/projectsettings.fbs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// Project settings format +// +// (C) 2024 Individual contributors, see AUTHORS file +// +//------------------------------------------------------------------------------ +namespace App; +table GISettings +{ + update_budget: float = 1.0; + update_frequency: float = 0.0; +} + +table ProjectSettings +{ + gi_settings: GISettings; +} + +root_type ProjectSettings; +file_identifier "NPST"; +file_extension "npst"; \ No newline at end of file diff --git a/syswork/export.zip b/syswork/export.zip index dbd2fe5fb..d75363018 100644 Binary files a/syswork/export.zip and b/syswork/export.zip differ diff --git a/syswork/shaders/vk/decals_cluster.fx b/syswork/shaders/vk/decals_cluster.fx index 42ddbe330..d135f2b4d 100644 --- a/syswork/shaders/vk/decals_cluster.fx +++ b/syswork/shaders/vk/decals_cluster.fx @@ -1,5 +1,5 @@ //------------------------------------------------------------------------------ -// lights_cluster_cull.fxh +// decals_cluster.fx // (C) 2019 Individual contributors, see AUTHORS file //------------------------------------------------------------------------------ @@ -42,7 +42,7 @@ render_state EmissiveState DepthEnabled = false; }; -write rgba16f image2D Decals; +//write rgba16f image2D Decals; write rgba16f image2D DebugOutput; //------------------------------------------------------------------------------ diff --git a/syswork/shaders/vk/gltf.fx b/syswork/shaders/vk/gltf.fx index a4047bf95..3149d8729 100644 --- a/syswork/shaders/vk/gltf.fx +++ b/syswork/shaders/vk/gltf.fx @@ -107,7 +107,7 @@ psGLTF( vec4 metallicRoughness = sample2D(_GLTF.metallicRoughnessTexture, MaterialSampler, UV) * vec4(1.0f, _GLTF.roughnessFactor, _GLTF.metallicFactor, 1.0f); vec4 emissive = sample2D(_GLTF.emissiveTexture, MaterialSampler, UV) * _GLTF.emissiveFactor; vec4 occlusion = sample2D(_GLTF.occlusionTexture, MaterialSampler, UV); - vec4 normals = sample2D(_GLTF.normalTexture, NormalSampler, UV); + vec4 normals = sample2D(_GLTF.normalTexture, NormalSampler, UV) * _GLTF.normalScale; vec4 material; material[MAT_METALLIC] = metallicRoughness.b; material[MAT_ROUGHNESS] = metallicRoughness.g; @@ -118,13 +118,9 @@ psGLTF( vec3 viewVec = normalize(EyePos.xyz - WorldSpacePos.xyz); vec3 F0 = CalculateF0(baseColor.rgb, material[MAT_METALLIC], vec3(0.04)); - float viewDepth = CalculateViewDepth(View, WorldSpacePos); - uint3 index3D = CalculateClusterIndex(gl_FragCoord.xy / BlockSize, viewDepth, InvZScale, InvZBias); - uint idx = Pack3DTo1D(index3D, NumCells.x, NumCells.y); - vec3 light = vec3(0, 0, 0); light += CalculateLight(WorldSpacePos, gl_FragCoord.xyz, baseColor.rgb, material, N); - light += calcEnv(baseColor, F0, N, viewVec, material); + //light += calcEnv(baseColor, F0, N, viewVec, material); light += emissive.rgb; OutColor = finalizeColor(light.rgb, baseColor.a); diff --git a/syswork/shaders/vk/lib/ddgi.fxh b/syswork/shaders/vk/lib/ddgi.fxh new file mode 100644 index 000000000..78af10553 --- /dev/null +++ b/syswork/shaders/vk/lib/ddgi.fxh @@ -0,0 +1,472 @@ +//------------------------------------------------------------------------------ +// @file ddgi.fxh +// @copyright (C) 2024 Individual contributors, see AUTHORS file +//------------------------------------------------------------------------------ +#ifndef DDGI_FXH +#define DDGI_FXH +#include + +const uint PROBE_STATE_INACTIVE = 0; +const uint PROBE_STATE_ACTIVE = 1; +const uint DDGI_NUM_FIXED_RAYS = 32; + +const uint NUM_IRRADIANCE_TEXELS_PER_PROBE = 6; +const uint NUM_DISTANCE_TEXELS_PER_PROBE = 14; + +const uint RELOCATION_OPTION = 0x1; +const uint SCROLL_OPTION = 0x2; +const uint CLASSIFICATION_OPTION = 0x4; +const uint LOW_PRECISION_IRRADIANCE_OPTION = 0x8; +const uint PARTIAL_UPDATE_OPTION = 0x10; + +//------------------------------------------------------------------------------ +/** +*/ +int +DDGIProbesPerPlane(ivec3 probeGridCounts) +{ + return probeGridCounts.x * probeGridCounts.z; +} + +//------------------------------------------------------------------------------ +/** +*/ +int +DDGIProbeIndex(ivec2 texCoord, ivec3 probeGridCounts) +{ + return texCoord.x + (texCoord.y * (probeGridCounts.x * probeGridCounts.y)); +} + +//------------------------------------------------------------------------------ +/** +*/ +int +DDGIProbeIndex(ivec3 probeCoords, ivec3 probeGridCounts) +{ + return probeCoords.x + (probeGridCounts.x * probeCoords.z) + (probeGridCounts.x * probeGridCounts.z) * probeCoords.y; +} + +//------------------------------------------------------------------------------ +/** +*/ +int +DDGIProbeIndex(uvec2 threadCoords, ivec3 probeGridCounts, int numTexels) +{ + int probesPerPlane = DDGIProbesPerPlane(probeGridCounts); + int planeIndex = int(threadCoords.x / (probeGridCounts.x * numTexels)); + int probeIndexInPlane = int(threadCoords.x / numTexels) - (planeIndex * probeGridCounts.x) + (probeGridCounts.x * int(threadCoords.y / numTexels)); + return planeIndex * probesPerPlane + probeIndexInPlane; +} + +//------------------------------------------------------------------------------ +/** +*/ +ivec2 +DDGIProbeTexelPosition(int probeIndex, ivec3 probeGridCounts) +{ + return ivec2(probeIndex % (probeGridCounts.x * probeGridCounts.y), probeIndex / (probeGridCounts.x * probeGridCounts.y)); +} + + +//------------------------------------------------------------------------------ +/** +*/ +ivec3 +DDGIProbeCoords(int probeIndex, ivec3 probeGridCounts) +{ + ivec3 ret; + ret.x = probeIndex % probeGridCounts.x; + ret.y = probeIndex / (probeGridCounts.x * probeGridCounts.z); + ret.z = (probeIndex / probeGridCounts.x) % probeGridCounts.z; + return ret; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec2 +DDGIProbeUV(int probeIndex, vec2 octantCoords, ivec3 probeGridCounts, int numTexels, ivec3 probeScrollOffsets, uint options) +{ + int probesPerPlane = DDGIProbesPerPlane(probeGridCounts); + int planeIndex = probeIndex / probesPerPlane; + + float probeInteriorTexels = float(numTexels); + float probeTexels = probeInteriorTexels * 2; + + int gridSpaceX = probeIndex % probeGridCounts.x; + int gridSpaceY = probeIndex / probeGridCounts.x; + + if ((options & SCROLL_OPTION) != 0) + { + gridSpaceX = (gridSpaceX + probeScrollOffsets.x) % probeGridCounts.x; + gridSpaceY = (gridSpaceY + probeScrollOffsets.z) % probeGridCounts.z; + planeIndex = (planeIndex + probeScrollOffsets.y) % probeGridCounts.y; + } + + int x = gridSpaceX + (planeIndex * probeGridCounts.x); + int y = gridSpaceY % probeGridCounts.z; + + float textureWidth = probeTexels * (probeGridCounts.x * probeGridCounts.y); + float textureHeight = probeTexels * probeGridCounts.z; + + vec2 uv = vec2(x * probeTexels, y * probeTexels) + (probeTexels * 0.5f); + uv += octantCoords.xy * (probeInteriorTexels * 0.5f); + uv /= vec2(textureWidth, textureHeight); + return uv; +} + +//------------------------------------------------------------------------------ +/** +*/ +ivec3 +DDGIBaseProbeGridCoordinates(vec3 worldPosition, vec3 volumeOrigin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing) +{ + vec3 position = worldPosition - volumeOrigin; + // TODO: handle rotations + + position += (probeGridSpacing * (probeGridCounts - 1)) * 0.5f; + ivec3 probeCoords = ivec3(position / probeGridSpacing); + + probeCoords = clamp(probeCoords, ivec3(0), (probeGridCounts - ivec3(1))); + + return probeCoords; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPosition(int3 probeCoords, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing) +{ + vec3 probeGridWorldPosition = probeCoords * probeGridSpacing; + vec3 probeGridShift = (probeGridSpacing * (probeGridCounts - 1)) * 0.5f; + + vec3 probeWorldPosition = probeGridWorldPosition - probeGridShift; + // TODO: ROTATE + probeWorldPosition += origin; + return probeWorldPosition; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPosition(int probeIndex, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing) +{ + ivec3 probeCoords = DDGIProbeCoords(probeIndex, probeGridCounts); + return DDGIProbeWorldPosition(probeCoords, origin, rotation, probeGridCounts, probeGridSpacing); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIDecodeProbeOffsets(ivec2 probeOffsetTexcoord, vec3 probeGridSpacing, uint probeOffsets) +{ + return fetch2D(probeOffsets, Basic2DSampler, probeOffsetTexcoord, 0).xyz * probeGridSpacing; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPositionWithOffset(int probeIndex, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing, uint probeOffsets) +{ + int textureWidth = probeGridCounts.x * probeGridCounts.y; + ivec2 offsetTexCoords = ivec2(probeIndex % textureWidth, probeIndex / textureWidth); + return DDGIDecodeProbeOffsets(offsetTexCoords, probeGridSpacing, probeOffsets) + DDGIProbeWorldPosition(probeIndex, origin, rotation, probeGridCounts, probeGridSpacing); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPositionWithOffset(ivec3 probeCoords, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing, uint probeOffsets) +{ + int probeIndex = DDGIProbeIndex(probeCoords, probeGridCounts); + return DDGIProbeWorldPositionWithOffset(probeIndex, origin, rotation, probeGridCounts, probeGridSpacing, probeOffsets); +} + +//------------------------------------------------------------------------------ +/** +*/ +int +DDGIProbeIndexOffset(int baseProbeIndex, ivec3 probeGridCounts, ivec3 probeScrollOffsets) +{ + ivec3 probeGridCoord = DDGIProbeCoords(baseProbeIndex, probeGridCounts); + ivec3 offsetProbeGridCoord = (probeGridCoord + probeScrollOffsets) % probeGridCounts; + int offsetProbeIndex = DDGIProbeIndex(offsetProbeGridCoord, probeGridCounts); + return offsetProbeIndex; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPositionWithScrollAndOffset(int probeIndex, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing, ivec3 probeScrollOffsets, uint probeOffsets) +{ + int textureWidth = probeGridCounts.x * probeGridCounts.y; + int storageProbeIndex = DDGIProbeIndexOffset(probeIndex, probeGridCounts, probeScrollOffsets); + ivec2 offsetTexCoords = ivec2(storageProbeIndex % textureWidth, storageProbeIndex / textureWidth); + return DDGIDecodeProbeOffsets(offsetTexCoords, probeGridSpacing, probeOffsets) + DDGIProbeWorldPosition(probeIndex, origin, rotation, probeGridCounts, probeGridSpacing); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIProbeWorldPositionWithScrollAndOffset(ivec3 probeCoords, vec3 origin, vec4 rotation, ivec3 probeGridCounts, vec3 probeGridSpacing, ivec3 probeScrollOffsets, uint probeOffsets) +{ + int probeIndex = DDGIProbeIndex(probeCoords, probeGridCounts); + return DDGIProbeWorldPositionWithScrollAndOffset(probeIndex, origin, rotation, probeGridCounts, probeGridSpacing, probeScrollOffsets, probeOffsets); +} + +//------------------------------------------------------------------------------ +/** +*/ +float +DDGISignNotZero(float v) +{ + return v >= 0.0f ? 1.0f : -1.0f; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec2 +DDGISignNotZero(vec2 v) +{ + return vec2(DDGISignNotZero(v.x), DDGISignNotZero(v.y)); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGISurfaceBias(vec3 normal, vec3 cameraDirection, float normalBias, float viewBias) +{ + return (normal * normalBias) + (-cameraDirection * viewBias); +} + +//------------------------------------------------------------------------------ +/** + Finds the smallest component of the vector. +*/ +float +DDGIMinComponent(vec3 a) +{ + return min(a.x, min(a.y, a.z)); +} + +//------------------------------------------------------------------------------ +/** + Finds the largest component of the vector. +*/ +float +DDGIMaxComponent(vec3 a) +{ + return max(a.x, max(a.y, a.z)); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec2 +DDGINormalizedOctahedralCoordinates(ivec2 threadCoords, int numTexels) +{ + vec2 octahedralTexelCoord = vec2(threadCoords.x % numTexels, threadCoords.y % numTexels); + octahedralTexelCoord.xy += 0.5f; + octahedralTexelCoord.xy /= float(numTexels); + octahedralTexelCoord *= 2.0f; + octahedralTexelCoord -= vec2(1.0f); + return octahedralTexelCoord; +} + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +DDGIOctahedralDirection(vec2 coords) +{ + vec3 direction = vec3(coords.x, coords.y, 1.0f - abs(coords.x) - abs(coords.y)); + if (direction.z < 0.0f) + { + direction.xy = (1.0f - abs(direction.yx)) * DDGISignNotZero(direction.xy); + } + return normalize(direction); +} + +//------------------------------------------------------------------------------ +/** +*/ +vec2 +DDGIOctahedralCoordinates(vec3 direction) +{ + float l1norm = abs(direction.x) + abs(direction.y) + abs(direction.z); + vec2 uv = direction.xy * (1.0f / l1norm); + if (direction.z < 0.0f) + { + uv = (1.0f - abs(uv.yx)) * DDGISignNotZero(direction.xy); + } + return uv; +} + +struct DDGIVolume +{ + vec3 origin; + vec4 rotationQuat; + ivec3 probeGridCounts; + vec3 probeGridSpacing; + ivec3 probeScrollOffsets; + int numIrradianceTexels; + int numDistanceTexels; + float probeIrradianceGamma; + uint irradianceHandle; + uint distanceHandle; + uint offsetsHandle; + uint statesHandle; +}; + +//------------------------------------------------------------------------------ +/** +*/ +vec3 +EvaluateDDGIIrradiance( + vec3 worldPosition, + vec3 surfaceBias, + vec3 direction, + GIVolume volume, + uint options +) +{ + vec3 irradiance = vec3(0, 0, 0); + float accumulatedWeights = 0.0f; + + vec3 biasedWorldPosition = worldPosition + surfaceBias; + ivec3 baseProbeCoordinates = DDGIBaseProbeGridCoordinates(biasedWorldPosition, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing); + vec3 baseProbeWorldPosition = DDGIProbeWorldPosition(baseProbeCoordinates, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing); + + vec3 distanceVolumeSpace = biasedWorldPosition - baseProbeWorldPosition; + // TODO: ROTATE + + vec3 alpha = clamp(distanceVolumeSpace / volume.GridSpacing, vec3(0), vec3(1)); + for (int probeIndex = 0; probeIndex < 8; probeIndex++) + { + ivec3 adjacentProbeOffset = ivec3(probeIndex, probeIndex >> 1, probeIndex >> 2) & ivec3(1); + ivec3 adjacentProbeCoords = clamp(baseProbeCoordinates + adjacentProbeOffset, ivec3(0), volume.GridCounts - ivec3(1)); + float3 adjacentProbeWorldPosition; + + if ((options & RELOCATION_OPTION) != 0) + { + if ((options & SCROLL_OPTION) != 0) + { + adjacentProbeWorldPosition = DDGIProbeWorldPositionWithScrollAndOffset(adjacentProbeCoords, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing, volume.ScrollOffsets, volume.Offsets); + } + else + { + //adjacentProbeWorldPosition = DDGIProbeWorldPosition(adjacentProbeCoords, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing); + adjacentProbeWorldPosition = DDGIProbeWorldPositionWithOffset(adjacentProbeCoords, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing, volume.Offsets); + } + } + else + { + adjacentProbeWorldPosition = DDGIProbeWorldPosition(adjacentProbeCoords, volume.Offset, volume.Rotation, volume.GridCounts, volume.GridSpacing); + } + + int adjacentProbeIndex = DDGIProbeIndex(adjacentProbeCoords, volume.GridCounts); + + if ((options & CLASSIFICATION_OPTION) != 0) + { + int probeIndex; + if ((options & SCROLL_OPTION) != 0) + { + probeIndex = DDGIProbeIndexOffset(adjacentProbeIndex, volume.GridCounts, volume.ScrollOffsets); + } + else + { + probeIndex = adjacentProbeIndex; + } + ivec2 texelPosition = DDGIProbeTexelPosition(probeIndex, volume.GridCounts); + int state = floatBitsToInt(fetch2D(volume.States, Basic2DSampler, texelPosition, 0).x); + if (state == PROBE_STATE_INACTIVE) + continue; + } + + vec3 worldPosToAdjacentProbe = normalize(adjacentProbeWorldPosition - worldPosition); + vec3 biasedPosToAdjacentProbe = normalize(adjacentProbeWorldPosition - biasedWorldPosition); + float biasedPosToAdjustedProbeDistance = length(adjacentProbeWorldPosition - biasedWorldPosition); + + vec3 trilinear = max(vec3(0.001f), lerp(1.0f - alpha, alpha, vec3(adjacentProbeOffset))); + float trilinearWeight = (trilinear.x * trilinear.y * trilinear.z); + float weight = 1.0f; + + float wrapShading = (dot(worldPosToAdjacentProbe, direction) + 1.0f) * 0.5f; + weight *= (wrapShading * wrapShading) + 0.2f; + + vec2 octantCoords = DDGIOctahedralCoordinates(-biasedPosToAdjacentProbe); + vec2 probeTextureCoords; + if ((options & SCROLL_OPTION) != 0) + { + probeTextureCoords = DDGIProbeUV(adjacentProbeIndex, octantCoords, volume.GridCounts, volume.NumDistanceTexels, volume.ScrollOffsets, options); + } + else + { + probeTextureCoords = DDGIProbeUV(adjacentProbeIndex, octantCoords, volume.GridCounts, volume.NumDistanceTexels, ivec3(0), options); + } + vec2 filteredDistance = sample2DLod(volume.Distances, Basic2DSampler, probeTextureCoords, 0).xy; + + float meanDistanceToSurface = filteredDistance.x; + float variance = abs((filteredDistance.x * filteredDistance.x) - filteredDistance.y); + + float chebyshevWeight = 1.0f; + if (biasedPosToAdjustedProbeDistance > meanDistanceToSurface) + { + float v = biasedPosToAdjustedProbeDistance - meanDistanceToSurface; + chebyshevWeight = variance / (variance + (v*v)); + + chebyshevWeight = max((chebyshevWeight * chebyshevWeight * chebyshevWeight), 0.0f); + } + + weight *= max(0.05f, chebyshevWeight); + weight = max(0.000001f, weight); + + const float crushThreshold = 0.2f; + if (weight < crushThreshold) + { + weight *= (weight * weight) * (1.0f / (crushThreshold * crushThreshold)); + } + + weight *= trilinearWeight; + + octantCoords = DDGIOctahedralCoordinates(direction); + if ((options & SCROLL_OPTION) != 0) + { + probeTextureCoords = DDGIProbeUV(adjacentProbeIndex, octantCoords, volume.GridCounts, volume.NumIrradianceTexels, volume.ScrollOffsets, options); + } + else + { + probeTextureCoords = DDGIProbeUV(adjacentProbeIndex, octantCoords, volume.GridCounts, volume.NumIrradianceTexels, ivec3(0), options); + } + vec3 probeIrradiance = sample2DLod(volume.Irradiance, Basic2DSampler, probeTextureCoords, 0).xyz; + + vec3 exponent = vec3(volume.EncodingGamma * 0.5f); + probeIrradiance = pow(probeIrradiance, exponent); + + irradiance += (weight * probeIrradiance); + accumulatedWeights += weight; + } + + if (accumulatedWeights == 0.0f) + return vec3(0); + + irradiance *= (1.0f / accumulatedWeights); + irradiance *= irradiance; + irradiance *= PI * 2.0f; + + if ((options & LOW_PRECISION_IRRADIANCE_OPTION) != 0) + irradiance *= 1.0989f; + + return irradiance; +} + +#endif // DDGI_FXH diff --git a/syswork/shaders/vk/lib/defaultsamplers.fxh b/syswork/shaders/vk/lib/defaultsamplers.fxh index 4b391dc3c..a75872103 100644 --- a/syswork/shaders/vk/lib/defaultsamplers.fxh +++ b/syswork/shaders/vk/lib/defaultsamplers.fxh @@ -18,7 +18,7 @@ sampler_state MaterialSampler sampler_state NormalSampler { - Filter = MinMagLinearMipPoint; + Filter = MinMagMipLinear; }; sampler_state CubeSampler diff --git a/syswork/shaders/vk/lib/lighting_functions.fxh b/syswork/shaders/vk/lib/lighting_functions.fxh index 36b50eb23..e1f5e57a8 100644 --- a/syswork/shaders/vk/lib/lighting_functions.fxh +++ b/syswork/shaders/vk/lib/lighting_functions.fxh @@ -7,6 +7,7 @@ #include "ltc.fxh" #include "CSM.fxh" #include "clustering.fxh" +#include "ddgi.fxh" // match these in lightcontext.cc const uint USE_SHADOW_BITFLAG = 0x1; @@ -221,6 +222,7 @@ CalculateRectLight( , in vec3 viewVec , in vec3 normal , in vec4 material + , in vec3 albedo , in bool twoSided ) { @@ -260,7 +262,7 @@ CalculateRectLight( vec3 spec = vec3(LtcRectIntegrate(normal, viewVec, pos, minv, points, true, twoSided)); // Integrate diffuse - vec3 diff = vec3(LtcRectIntegrate(normal, viewVec, pos, mat3(1), points, false, twoSided)); + vec3 diff = vec3(LtcRectIntegrate(normal, viewVec, pos, mat3(1), points, false, twoSided)) * albedo; return li.color * (spec + diff) * attenuation; } @@ -275,6 +277,7 @@ CalculateDiskLight( , in vec3 viewVec , in vec3 normal , in vec4 material + , in vec3 albedo , in bool twoSided ) { @@ -314,7 +317,7 @@ CalculateDiskLight( vec3 spec = vec3(LtcDiskIntegrate(normal, viewVec, pos, minv, points, true, twoSided)); // Integrate diffuse - vec3 diff = vec3(LtcDiskIntegrate(normal, viewVec, pos, mat3(1), points, false, twoSided)); + vec3 diff = vec3(LtcDiskIntegrate(normal, viewVec, pos, mat3(1), points, false, twoSided)) * albedo; return li.color * (diff + spec) * attenuation; } @@ -329,6 +332,7 @@ CalculateTubeLight( , in vec3 viewVec , in vec3 normal , in vec4 material + , in vec3 albedo , in bool twoSided ) { @@ -364,7 +368,7 @@ CalculateTubeLight( vec3 spec = vec3(LtcLineIntegrate(normal, viewVec, pos, li.radius, minv, points)); // Integrate diffuse - vec3 diff = vec3(LtcLineIntegrate(normal, viewVec, pos, li.radius, mat3(1), points)); + vec3 diff = vec3(LtcLineIntegrate(normal, viewVec, pos, li.radius, mat3(1), points)) * albedo; return li.color * (spec + diff) * (1.0f / 2 * PI) * attenuation; } @@ -390,18 +394,23 @@ CalculateGlobalLight(vec3 diffuseColor, vec4 material, vec3 F0, vec3 viewVec, ve float shadowFactor = 1.0f; if (FlagSet(GlobalLightFlags, USE_SHADOW_BITFLAG)) { - vec4 shadowPos = CSMShadowMatrix * vec4(worldSpacePosition, 1); // csm contains inversed view + csm transform + // CSM transform takes us from world space to light view space + vec4 shadowPos = CSMShadowMatrix * vec4(worldSpacePosition, 1); shadowFactor = CSMPS(shadowPos, GlobalLightShadowBuffer #ifdef CSM_DEBUG , csmDebug #endif ); - vec2 terrainUv = mad(worldSpacePosition.xz, InvTerrainSize, vec2(0.5f)); - //shadowFactor *= sample2DLod(TerrainShadowBuffer, CSMTextureSampler, terrainUv, 0).r; - vec2 terrainShadow = TerrainShadows(TerrainShadowBuffer, terrainUv, TerrainShadowMapPixelSize); - float blend = abs(worldSpacePosition.y - terrainShadow.y * 0.8f) / (terrainShadow.y - terrainShadow.y * 0.8f); - shadowFactor *= terrainShadow.x * blend; + if (EnableTerrainShadows == 1) + { + vec2 terrainUv = mad(worldSpacePosition.xz, InvTerrainSize, vec2(0.5f)); + //shadowFactor *= sample2DLod(TerrainShadowBuffer, CSMTextureSampler, terrainUv, 0).r; + vec2 terrainShadow = TerrainShadows(TerrainShadowBuffer, terrainUv, TerrainShadowMapPixelSize); + float blend = abs(worldSpacePosition.y - terrainShadow.y * 0.8f) / (terrainShadow.y - terrainShadow.y * 0.8f); + shadowFactor *= terrainShadow.x * blend; + } + //shadowFactor *= terrainShadow.x < 1.0f ? * terrainShadow.x : 1.0f; //shadowFactor *= lerp(1.0f, terrainShadow.x, smoothstep(terrainShadow.y * 0.8f, terrainShadow.y, worldSpacePosition.y)); @@ -435,11 +444,10 @@ CalculateGlobalLight(vec3 diffuseColor, vec4 material, vec3 F0, vec3 viewVec, ve @param depth The fragments depth (gl_FragCoord.z) */ vec3 -LocalLights(uint clusterIndex, vec3 diffuseColor, vec4 material, vec3 F0, vec3 pos, vec3 normal, float depth) +LocalLights(uint clusterIndex, vec3 viewVec, vec3 diffuseColor, vec4 material, vec3 F0, vec3 pos, vec3 normal, float depth) { vec3 light = vec3(0, 0, 0); uint flag = AABBs[clusterIndex].featureFlags; - vec3 viewVec = normalize(EyePos.xyz - pos); if (CHECK_FLAG(flag, CLUSTER_POINTLIGHT_BIT)) { // shade point lights @@ -514,6 +522,7 @@ LocalLights(uint clusterIndex, vec3 diffuseColor, vec4 material, vec3 F0, vec3 p , viewVec , normal , material + , diffuseColor , CHECK_FLAG(li.flags, AREA_LIGHT_TWOSIDED) ); } @@ -525,6 +534,7 @@ LocalLights(uint clusterIndex, vec3 diffuseColor, vec4 material, vec3 F0, vec3 p , viewVec , normal , material + , diffuseColor , CHECK_FLAG(li.flags, AREA_LIGHT_TWOSIDED) ); } @@ -536,6 +546,7 @@ LocalLights(uint clusterIndex, vec3 diffuseColor, vec4 material, vec3 F0, vec3 p , viewVec , normal , material + , diffuseColor , CHECK_FLAG(li.flags, AREA_LIGHT_TWOSIDED) ); } @@ -544,6 +555,44 @@ LocalLights(uint clusterIndex, vec3 diffuseColor, vec4 material, vec3 F0, vec3 p return light; } +//------------------------------------------------------------------------------ +/** +*/ +vec3 +GI(uint clusterIndex, vec3 viewVec, vec3 pos, vec3 normal, vec3 albedo) +{ + vec3 accumulatedGi = vec3(0, 0, 0); + uint flag = AABBs[clusterIndex].featureFlags; + if (CHECK_FLAG(flag, CLUSTER_GI_VOLUME_BIT)) + { + uint count = GIVolumeCountList[clusterIndex]; + for (int i = 0; i < count; i++) + { + uint lidx = GIVolumeIndexLists[clusterIndex * MAX_GI_VOLUMES_PER_CLUSTER + i]; + GIVolume gi = GIVolumes[lidx]; + vec3 relativePosition = pos - gi.Offset; + /// TODO: Rotate + if (relativePosition.x > gi.Size.x || relativePosition.y > gi.Size.y || relativePosition.z > gi.Size.z) + continue; + + + vec3 edgeDistance = gi.Size - abs(relativePosition); + float edgeMinDistance = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z); + float weight = 0.0f; + if (gi.Blend == 0.0f) + weight = (edgeMinDistance < gi.BlendCutoff) ? 0.0f : 1.0f; + else + weight = clamp((edgeMinDistance - gi.BlendCutoff) / gi.Blend, 0.0f, 1.0f); + + vec3 surfaceBias = DDGISurfaceBias(normal, viewVec, gi.NormalBias, gi.ViewBias); + //light += vec3(1,0,0); + vec3 volumeGI = max(vec3(0), EvaluateDDGIIrradiance(pos, surfaceBias, normal, gi, gi.Options) * (albedo / PI) / gi.IrradianceScale); + accumulatedGi = mix(accumulatedGi, volumeGI, vec3(weight)); + } + } + return accumulatedGi; +} + //------------------------------------------------------------------------------ /** */ @@ -575,6 +624,7 @@ CalculatePointLightAmbientTransmission( float shadowFactor = 1.0f; if (FlagSet(light.flags, USE_SHADOW_BITFLAG)) { + // TODO: The View here must be inferred from the view vector instead of relying the camera matrix vec3 projDir = (InvView * vec4(-lightDir, 0)).xyz; shadowFactor = GetInvertedOcclusionPointLight(depth, projDir, ext.shadowMap); shadowFactor = saturate(lerp(1.0f, saturate(shadowFactor), ext.shadowIntensity)); @@ -754,7 +804,8 @@ CalculateLight(vec3 worldSpacePos, vec3 clipXYZ, vec3 albedo, vec4 material, vec // Check if all waves use the same index and do this super cheaply if (subgroupBallot(firstWaveIndex == idx) == execMask) { - light += LocalLights(firstWaveIndex, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += LocalLights(firstWaveIndex, viewVec, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += GI(firstWaveIndex, viewVec, worldSpacePos, normal, albedo); } else { @@ -769,12 +820,14 @@ CalculateLight(vec3 worldSpacePos, vec3 clipXYZ, vec3 albedo, vec4 material, vec // this will effectively scalarize the light lists if (scalarIdx == idx) { - light += LocalLights(scalarIdx, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += LocalLights(scalarIdx, viewVec, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += GI(scalarIdx, viewVec, worldSpacePos, normal, albedo); } } } #else - light += LocalLights(idx, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += LocalLights(idx, viewVec, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += GI(idx, viewVec, worldSpacePos, normal, albedo); #endif //light += IBL(albedo, F0, normal, viewVec, material); diff --git a/syswork/shaders/vk/lib/raytracing.fxh b/syswork/shaders/vk/lib/raytracing.fxh index 22fd2581b..e790cc9b3 100644 --- a/syswork/shaders/vk/lib/raytracing.fxh +++ b/syswork/shaders/vk/lib/raytracing.fxh @@ -2,6 +2,10 @@ // raytracing.fxh // (C) 2024 Individual contributors, see AUTHORS file //------------------------------------------------------------------------------ + +#ifndef RAYTRACING_FXH +#define RAYTRACING_FXH + #include #include #include @@ -9,11 +13,28 @@ #include group(BATCH_GROUP) accelerationStructure TLAS; -group(BATCH_GROUP) write rgba16f image2D RaytracingOutput; #define MESH_BINDING group(BATCH_GROUP) binding(50) #define OBJECT_BINDING group(BATCH_GROUP) binding(49) +const uint RAY_MISS_BIT = 0x1; +const uint RAY_BACK_FACE_BIT = 0x2; +const uint RAY_MATERIAL_TWO_SIDED_BIT = 0x4; + +struct LightResponsePayload +{ + vec3 radiance; + float alpha; + + vec3 normal; + float depth; + + vec3 albedo; + uint bits; + + vec4 material; +}; + struct HitResult { vec3 albedo; @@ -21,17 +42,17 @@ struct HitResult vec4 material; vec3 normal; float depth; - uint miss; + uint bits; }; // Declare type for vertex positions and uv -ptr alignment(16) struct VertexPosUv +ptr alignment(4) struct VertexPosUv { vec3 position; int uv; }; -ptr alignment(8) struct VertexAttributeNormals +ptr alignment(4) struct VertexAttributeNormals { ivec2 normal_tangent; }; @@ -55,11 +76,6 @@ ptr alignment(4) struct VertexAttributeSkin uint indices; }; -ptr alignment(4) struct VertexAttributeDummy -{ - uint dummy; -}; - ptr alignment(4) struct Indexes32 { uint index; @@ -83,12 +99,16 @@ MESH_BINDING rw_buffer Geometry struct Object { - VertexPosUv PositionsPtr; - VertexAttributeDummy AttrPtr; - Indexes16 IndexPtr; + uvec2 PositionsPtr; + uvec2 AttrPtr; + uvec2 IndexPtr; + uint AttributeStride; uint Use16BitIndex; uint MaterialOffset; uint VertexLayout; + uint BaseIndexOffset; + uint BaseVertexPositionOffset; + uint BaseVertexAttributeOffset; }; OBJECT_BINDING rw_buffer ObjectBuffer @@ -150,11 +170,7 @@ UnpackUV32(int packedUv) vec3 UnpackNormal32(int packedNormal) { - int x = packedNormal & 0xFF; - int y = (packedNormal >> 8) & 0xFF; - int z = (packedNormal >> 16) & 0xFF; - vec3 unpacked = vec3(x, y, z) / 128.0f; - return unpacked; + return unpackSnorm4x8(packedNormal).xyz; } //------------------------------------------------------------------------------ @@ -164,7 +180,20 @@ float UnpackSign(int packedNormal) { int sig = (packedNormal >> 24) & 0xFF; - return sig == 127 ? 1.0f : 0.0f; + return sig == 0x80 ? -1.0f : 1.0f; +} + + +//------------------------------------------------------------------------------ +/** +*/ +uvec2 +OffsetPointer(uvec2 basePtr, uint offset) +{ + uint carry; + uint lo = uaddCarry(basePtr.x, offset, carry); + uint hi = basePtr.y + carry; + return uvec2(lo, hi); } //------------------------------------------------------------------------------ @@ -173,18 +202,22 @@ UnpackSign(int packedNormal) void SampleTerrain(in Object obj, uint prim, in vec3 baryCoords, out uvec3 indices, out vec2 uv, out mat3 tbn) { - // Sample the index buffer if (obj.Use16BitIndex == 1) - indices = uvec3(obj.IndexPtr[prim * 3].index, obj.IndexPtr[prim * 3 + 1].index, obj.IndexPtr[prim * 3 + 2].index); + { + Indexes16 i16Ptr = Indexes16(OffsetPointer(obj.IndexPtr, obj.BaseIndexOffset)); + indices = uvec3(i16Ptr[prim * 3].index, i16Ptr[prim * 3 + 1].index, i16Ptr[prim * 3 + 2].index); + } else { - Indexes32 i32Ptr = Indexes32(obj.IndexPtr); - indices = uvec3(i32Ptr[prim * 3].index, i32Ptr[prim * 3 + 1].index, i32Ptr[prim * 3 + 2].index); + Indexes32 i32 = Indexes32(OffsetPointer(obj.IndexPtr, obj.BaseIndexOffset)); + indices = uvec3(i32[prim * 3].index, i32[prim * 3 + 1].index, i32[prim * 3 + 2].index); } + + VertexPosUv positionsPtr = VertexPosUv(OffsetPointer(obj.PositionsPtr, obj.BaseVertexPositionOffset)); - vec2 uv0 = UnpackUV32((obj.PositionsPtr[indices.x]).uv); - vec2 uv1 = UnpackUV32((obj.PositionsPtr[indices.y]).uv); - vec2 uv2 = UnpackUV32((obj.PositionsPtr[indices.z]).uv); + vec2 uv0 = UnpackUV32((positionsPtr[indices.x]).uv); + vec2 uv1 = UnpackUV32((positionsPtr[indices.y]).uv); + vec2 uv2 = UnpackUV32((positionsPtr[indices.z]).uv); uv = BaryCentricVec2(uv0, uv1, uv2, baryCoords); tbn = PlaneTBN(vec3(0, 1, 0)); @@ -194,62 +227,66 @@ SampleTerrain(in Object obj, uint prim, in vec3 baryCoords, out uvec3 indices, o /** */ void -SampleGeometry(in Object obj, uint prim, in vec3 baryCoords, out uvec3 indices, out vec2 uv, out mat3 tbn) +SampleGeometry(in Object obj, uint prim, in vec3 baryCoords, out uvec3 indices, out vec2 uv, out vec3 normal, out vec3 tangent, out float sign) { // Sample the index buffer if (obj.Use16BitIndex == 1) - indices = uvec3(obj.IndexPtr[prim * 3].index, obj.IndexPtr[prim * 3 + 1].index, obj.IndexPtr[prim * 3 + 2].index); + { + Indexes16 i16Ptr = Indexes16(OffsetPointer(obj.IndexPtr, obj.BaseIndexOffset)); + indices = uvec3(i16Ptr[prim * 3].index, i16Ptr[prim * 3 + 1].index, i16Ptr[prim * 3 + 2].index); + } else { - Indexes32 i32Ptr = Indexes32(obj.IndexPtr); - indices = uvec3(i32Ptr[prim * 3].index, i32Ptr[prim * 3 + 1].index, i32Ptr[prim * 3 + 2].index); + Indexes32 i32 = Indexes32(OffsetPointer(obj.IndexPtr, obj.BaseIndexOffset)); + indices = uvec3(i32[prim * 3].index, i32[prim * 3 + 1].index, i32[prim * 3 + 2].index); } + + VertexPosUv positionsPtr = VertexPosUv(OffsetPointer(obj.PositionsPtr, obj.BaseVertexPositionOffset)); - vec2 uv0 = UnpackUV32((obj.PositionsPtr[indices.x]).uv); - vec2 uv1 = UnpackUV32((obj.PositionsPtr[indices.y]).uv); - vec2 uv2 = UnpackUV32((obj.PositionsPtr[indices.z]).uv); + vec2 uv0 = UnpackUV32((positionsPtr[indices.x]).uv); + vec2 uv1 = UnpackUV32((positionsPtr[indices.y]).uv); + vec2 uv2 = UnpackUV32((positionsPtr[indices.z]).uv); uv = BaryCentricVec2(uv0, uv1, uv2, baryCoords); + uvec2 attribPtr = OffsetPointer(obj.AttrPtr, obj.BaseVertexAttributeOffset); + vec3 n1, n2, n3; vec3 t1, t2, t3; - float sign; - - switch (obj.VertexLayout) { case 1: // Normal { - VertexAttributeNormals attrs = VertexAttributeNormals(obj.AttrPtr); - n1 = UnpackNormal32(attrs[indices.x].normal_tangent.x); - n2 = UnpackNormal32(attrs[indices.y].normal_tangent.x); - n3 = UnpackNormal32(attrs[indices.z].normal_tangent.x); - t1 = UnpackNormal32(attrs[indices.x].normal_tangent.y); - t2 = UnpackNormal32(attrs[indices.y].normal_tangent.y); - t3 = UnpackNormal32(attrs[indices.z].normal_tangent.y); - sign = UnpackSign(attrs[indices.x].normal_tangent.y); + VertexAttributeNormals attrs1 = VertexAttributeNormals(OffsetPointer(attribPtr, indices.x * obj.AttributeStride)); + VertexAttributeNormals attrs2 = VertexAttributeNormals(OffsetPointer(attribPtr, indices.y * obj.AttributeStride)); + VertexAttributeNormals attrs3 = VertexAttributeNormals(OffsetPointer(attribPtr, indices.z * obj.AttributeStride)); + n1 = UnpackNormal32(attrs1.normal_tangent.x); + n2 = UnpackNormal32(attrs2.normal_tangent.x); + n3 = UnpackNormal32(attrs3.normal_tangent.x); + t1 = UnpackNormal32(attrs1.normal_tangent.y); + t2 = UnpackNormal32(attrs2.normal_tangent.y); + t3 = UnpackNormal32(attrs3.normal_tangent.y); + sign = UnpackSign(attrs1.normal_tangent.y); break; } case 4: // Skin { - VertexAttributeSkin attrs = VertexAttributeSkin(obj.AttrPtr); - n1 = UnpackNormal32(attrs[indices.x].normal_tangent.x); - n2 = UnpackNormal32(attrs[indices.y].normal_tangent.x); - n3 = UnpackNormal32(attrs[indices.z].normal_tangent.x); - t1 = UnpackNormal32(attrs[indices.x].normal_tangent.y); - t2 = UnpackNormal32(attrs[indices.y].normal_tangent.y); - t3 = UnpackNormal32(attrs[indices.z].normal_tangent.y); - sign = UnpackSign(attrs[indices.x].normal_tangent.y); + VertexAttributeSkin attrs1 = VertexAttributeSkin(OffsetPointer(attribPtr, indices.x * obj.AttributeStride)); + VertexAttributeSkin attrs2 = VertexAttributeSkin(OffsetPointer(attribPtr, indices.y * obj.AttributeStride)); + VertexAttributeSkin attrs3 = VertexAttributeSkin(OffsetPointer(attribPtr, indices.z * obj.AttributeStride)); + n1 = UnpackNormal32(attrs1.normal_tangent.x); + n2 = UnpackNormal32(attrs2.normal_tangent.x); + n3 = UnpackNormal32(attrs3.normal_tangent.x); + t1 = UnpackNormal32(attrs1.normal_tangent.y); + t2 = UnpackNormal32(attrs2.normal_tangent.y); + t3 = UnpackNormal32(attrs3.normal_tangent.y); + sign = UnpackSign(attrs1.normal_tangent.y); break; } } - vec3 norm = BaryCentricVec3(n1, n2, n3, baryCoords); - vec3 tang = BaryCentricVec3(t1, t2, t3, baryCoords); - - tbn = TangentSpace(tang, norm, sign); + normal = BaryCentricVec3(n1, n2, n3, baryCoords); + tangent = BaryCentricVec3(t1, t2, t3, baryCoords); } -#define OFFSET_PTR(ptr, offset, type) type(VoidPtr(ptr) + offset) - //------------------------------------------------------------------------------ /** @@ -270,14 +307,14 @@ CalculateClusterIndexRT(vec2 screenPos, float depth, float scale, float bias) /** */ vec3 -CalculateLightRT(vec3 worldSpacePos, float depth, vec3 albedo, vec4 material, vec3 normal) +CalculateLightRT(vec3 worldSpacePos, vec3 origin, float depth, vec3 albedo, vec4 material, vec3 normal) { - vec3 clusterCenter = EyePos.xyz - worldSpacePos; - uvec3 index3D = uvec3(clusterCenter) / BlockSize.xxx; + vec3 clusterCenter = worldSpacePos - EyePos.xyz; + uvec3 index3D = ivec3((clusterCenter / BlockSize.yyy) + NumCells * 0.5f); uint idx = Pack3DTo1D(index3D, NumCells.x, NumCells.y); vec3 light = vec3(0, 0, 0); - vec3 viewVec = normalize(EyePos.xyz - worldSpacePos.xyz); + vec3 viewVec = normalize(origin.xyz - worldSpacePos.xyz); vec3 F0 = CalculateF0(albedo.rgb, material[MAT_METALLIC], vec3(0.04)); light += CalculateGlobalLight(albedo, material, F0, viewVec, normal, worldSpacePos); @@ -292,7 +329,7 @@ CalculateLightRT(vec3 worldSpacePos, float depth, vec3 albedo, vec4 material, ve // Check if all waves use the same index and do this super cheaply if (subgroupBallot(firstWaveIndex == idx) == execMask) { - light += LocalLights(firstWaveIndex, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += LocalLights(firstWaveIndex, viewVec, albedo, material, F0, worldSpacePos, normal, depth); } else { @@ -307,15 +344,14 @@ CalculateLightRT(vec3 worldSpacePos, float depth, vec3 albedo, vec4 material, ve // this will effectively scalarize the light lists if (scalarIdx == idx) { - light += LocalLights(scalarIdx, albedo, material, F0, worldSpacePos, normal, clipXYZ.z); + light += LocalLights(scalarIdx, viewVec, albedo, material, F0, worldSpacePos, normal, depth); } } } #else - light += LocalLights(idx, albedo, material, F0, worldSpacePos, normal, depth); + light += LocalLights(idx, viewVec, albedo, material, F0, worldSpacePos, normal, depth); #endif - - //light += IBL(albedo, F0, normal, viewVec, material); - light += albedo.rgb * material[MAT_EMISSIVE]; return light; -} \ No newline at end of file +} + +#endif // RAYTRACING_FXH \ No newline at end of file diff --git a/syswork/shaders/vk/lib/shared.fxh b/syswork/shaders/vk/lib/shared.fxh index 882a8c264..59ab9ad5c 100644 --- a/syswork/shaders/vk/lib/shared.fxh +++ b/syswork/shaders/vk/lib/shared.fxh @@ -86,6 +86,7 @@ group(TICK_GROUP) shared constant PerTickParams vec4 Balance; vec3 DoFDistances; + uint EnableTerrainShadows; vec3 BloomColor; float BloomIntensity; @@ -99,11 +100,9 @@ group(TICK_GROUP) shared constant PerTickParams vec2 GlobalLightShadowMapSize; vec4 GlobalLightDirWorldspace; vec4 GlobalLightColor; - vec4 GlobalBackLightColor; vec4 GlobalAmbientLightColor; mat4 CSMShadowMatrix; - float GlobalBackLightOffset; textureHandle GlobalLightShadowBuffer; textureHandle TerrainShadowBuffer; int NumEnvMips; @@ -169,7 +168,7 @@ group(FRAME_GROUP) constant ViewConstants vec4 Time_Random_Luminance_X; // x is time, y is random, z is luminance, w is unused }; -group(FRAME_GROUP) constant ShadowViewConstants[string Visibility = "VS|CS|PS|RGS";] +group(FRAME_GROUP) constant ShadowViewConstants[string Visibility = "VS|CS|PS|RCS";] { vec4 CascadeOffset[NUM_CASCADES]; vec4 CascadeScale[NUM_CASCADES]; @@ -211,9 +210,10 @@ const uint CLUSTER_PBR_DECAL_BIT = 0x10u; const uint CLUSTER_EMISSIVE_DECAL_BIT = 0x20u; const uint CLUSTER_FOG_SPHERE_BIT = 0x40u; const uint CLUSTER_FOG_BOX_BIT = 0x80u; +const uint CLUSTER_GI_VOLUME_BIT = 0x100u; // set a fixed number of cluster entries -const uint NUM_CLUSTER_ENTRIES = 16384; +const uint NUM_CLUSTER_ENTRIES = 262144; struct ClusterAABB { @@ -222,13 +222,13 @@ struct ClusterAABB uint featureFlags; }; -group(FRAME_GROUP) rw_buffer ClusterAABBs [ string Visibility = "CS|VS|PS|RGS"; ] +group(FRAME_GROUP) rw_buffer ClusterAABBs [ string Visibility = "CS|VS|PS|RCS"; ] { ClusterAABB AABBs[]; }; // this is used to keep track of how many lights we have active -group(FRAME_GROUP) constant ClusterUniforms [ string Visibility = "CS|VS|PS|RGS"; ] +group(FRAME_GROUP) constant ClusterUniforms [ string Visibility = "CS|VS|PS|RCS"; ] { vec2 FramebufferDimensions; vec2 InvFramebufferDimensions; @@ -296,9 +296,9 @@ struct PointLightShadowExtension struct AreaLight { - vec3 bboxMin; // Bounding box min point + vec3 center; float range; - vec3 bboxMax; // Bounding box max point + vec3 extents; float radius; vec3 xAxis; @@ -330,7 +330,7 @@ group(FRAME_GROUP) constant LightUniforms [ string Visibility = "CS|VS|PS"; ] }; // contains amount of lights, and the index of the light (pointing to the indices in PointLightList and SpotLightList), to output -group(FRAME_GROUP) rw_buffer LightIndexLists[string Visibility = "CS|VS|PS|RGS";] +group(FRAME_GROUP) rw_buffer LightIndexLists[string Visibility = "CS|VS|PS|RCS";] { uint PointLightCountList[NUM_CLUSTER_ENTRIES]; uint PointLightIndexList[NUM_CLUSTER_ENTRIES * MAX_LIGHTS_PER_CLUSTER]; @@ -340,7 +340,7 @@ group(FRAME_GROUP) rw_buffer LightIndexLists[string Visibility = "CS|VS|PS|RGS"; uint AreaLightIndexList[NUM_CLUSTER_ENTRIES * MAX_LIGHTS_PER_CLUSTER]; }; -group(FRAME_GROUP) rw_buffer LightLists[string Visibility = "CS|VS|PS|RGS";] +group(FRAME_GROUP) rw_buffer LightLists[string Visibility = "CS|VS|PS|RCS";] { SpotLight SpotLights[1024]; SpotLightProjectionExtension SpotLightProjection[256]; @@ -462,6 +462,63 @@ group(FRAME_GROUP) rw_buffer FogLists [ string Visibility = "CS|VS|PS"; ] FogBox FogBoxes[128]; }; +//------------------------------------------------------------------------------ +/** + GI +*/ +//------------------------------------------------------------------------------ +struct GIVolume +{ + vec3 bboxMin; + int NumDistanceTexels; + vec3 bboxMax; + float EncodingGamma; + vec3 Offset; + textureHandle Irradiance; + vec4 Rotation; // quaternion + ivec3 GridCounts; + textureHandle Distances; + vec3 GridSpacing; + textureHandle Offsets; + ivec3 ScrollOffsets; + int NumIrradianceTexels; + vec3 Size; + textureHandle States; + + float BlendCutoff; + float Blend; + + float NormalBias; + float ViewBias; + float IrradianceScale; + uint Options; +}; + +#define MAX_GI_VOLUMES_PER_CLUSTER 4 + +// this is used to keep track of how many lights we have active +group(FRAME_GROUP) constant GIVolumeUniforms [ string Visibility = "CS|PS"; ] +{ + uint NumGIVolumes; + uint NumGIVolumeClusters; +}; + +group(FRAME_GROUP) rw_buffer GIIndexLists [ string Visibility = "CS|VS|PS"; ] +{ + uint GIVolumeCountList[NUM_CLUSTER_ENTRIES]; + uint GIVolumeIndexLists[NUM_CLUSTER_ENTRIES * MAX_GI_VOLUMES_PER_CLUSTER]; +}; + +group(FRAME_GROUP) rw_buffer GIVolumeLists [ string Visibility = "CS|VS|PS"; ] +{ + GIVolume GIVolumes[64]; +}; + +//------------------------------------------------------------------------------ +/** + Render targets +*/ +//------------------------------------------------------------------------------ group(PASS_GROUP) inputAttachment InputAttachment0; group(PASS_GROUP) inputAttachment InputAttachment1; group(PASS_GROUP) inputAttachment InputAttachment2; diff --git a/syswork/shaders/vk/lib/standard_shading.fxh b/syswork/shaders/vk/lib/standard_shading.fxh index 747a960d9..d2f3567a8 100644 --- a/syswork/shaders/vk/lib/standard_shading.fxh +++ b/syswork/shaders/vk/lib/standard_shading.fxh @@ -54,6 +54,7 @@ psStandard( { vec4 albedo = calcColor(sample2D(_BRDF.AlbedoMap, MaterialSampler, UV)) * _BRDF.MatAlbedoIntensity; vec4 material = calcMaterial(sample2D(_BRDF.ParameterMap, MaterialSampler, UV)); + material[MAT_EMISSIVE] = 0.0f; vec3 N = normalize(calcBump(Tangent, Normal, Sign, sample2D(_BRDF.NormalMap, NormalSampler, UV))); //ApplyDecals(idx, ViewSpacePos, vec4(WorldSpacePos, 1), gl_FragCoord.z, albedo, N, material); @@ -62,7 +63,8 @@ psStandard( vec3 light = vec3(0, 0, 0); light += CalculateLight(WorldSpacePos, gl_FragCoord.xyz, albedo.rgb, material, N); - light += calcEnv(albedo, F0, N, viewVec, material); + + //light += calcEnv(albedo, F0, N, viewVec, material); OutColor = finalizeColor(light.rgb, albedo.a); //OutNormal = vec4(N, 0); diff --git a/syswork/shaders/vk/lights_cluster.fx b/syswork/shaders/vk/lights_cluster.fx index 2d5bff336..1a7280ab2 100644 --- a/syswork/shaders/vk/lights_cluster.fx +++ b/syswork/shaders/vk/lights_cluster.fx @@ -71,9 +71,14 @@ void csCull() numLights = 0; for (uint i = 0; i < NumAreaLights; i++) - { + { const AreaLight light = AreaLights[i]; - if (TestAABBAABB(aabb, light.bboxMin, light.bboxMax)) + vec3 viewSpaceCenter = (View * vec4(light.center, 1)).xyz; + + //vec3 maxExtents = vec3(max(max(viewSpaceExtents.x, viewSpaceExtents.y), viewSpaceExtents.z)); + vec3 viewSpaceMin = viewSpaceCenter - light.extents; + vec3 viewSpaceMax = viewSpaceCenter + light.extents; + if (TestAABBAABB(aabb, viewSpaceMin, viewSpaceMax)) { AreaLightIndexList[index1D * MAX_LIGHTS_PER_CLUSTER + numLights] = i; numLights++; diff --git a/tests/testtbui/main.cc b/tests/testtbui/main.cc index b5b684127..2bc8932a6 100644 --- a/tests/testtbui/main.cc +++ b/tests/testtbui/main.cc @@ -19,7 +19,7 @@ #include "nflatbuffer/nebula_flat.h" #include "nflatbuffer/flatbufferinterface.h" -#include "flat/graphicsfeature/terrainschema.h" +#include "flat/options/levelsettings.h" ImplementNebulaApplication(); @@ -47,9 +47,8 @@ class NebulaDemoApplication : public App::GameApplication this->gameServer->AttachGameFeature(this->editorFeatureUnit); #endif - Flat::FlatbufferInterface::LoadSchema("data:flatbuffer/graphicsfeature/terrainschema.bfbs"_uri); - IO::URI tablePath = "proj:work/data/tables/terrain.json"_uri; - CompileFlatbuffer(GraphicsFeature::TerrainSetup, tablePath, "data:tables/graphicsfeature"); + IO::URI tablePath = "proj:work/data/tables/base_level.json"_uri; + CompileFlatbuffer(App::LevelSettings, tablePath, "tbl:app"); } /// cleanup game features void CleanupGameFeatures() diff --git a/tests/testviewer/viewerapp.cc b/tests/testviewer/viewerapp.cc index e7d76d8b5..a90550a90 100644 --- a/tests/testviewer/viewerapp.cc +++ b/tests/testviewer/viewerapp.cc @@ -223,8 +223,6 @@ SimpleViewerApplication::Open() Math::vec3(1, 1, 1), 10.0f, Math::vec3(0, 0, 0), - Math::vec3(0, 0, 0), - 0.0f, 60_rad, 0_rad, true diff --git a/tests/testvisibility/visibilitytest.cc b/tests/testvisibility/visibilitytest.cc index e1e1bcb2c..434c46d89 100644 --- a/tests/testvisibility/visibilitytest.cc +++ b/tests/testvisibility/visibilitytest.cc @@ -122,7 +122,7 @@ VisibilityTest::Run() GraphicsEntityId globalLight = Graphics::CreateEntity(); Lighting::LightContext::RegisterEntity(globalLight); - Lighting::LightContext::SetupGlobalLight(globalLight, Math::vec3(1, 1, 1), 1.0f, Math::vec3(0, 0, 0), Math::vec3(0, 0, 0), 0.0f, 85_rad, 0_rad, true); + Lighting::LightContext::SetupGlobalLight(globalLight, Math::vec3(1, 1, 1), 1.0f, Math::vec3(0, 0, 0), 85_rad, 0_rad, true); // register visibility system ObserverContext::CreateBruteforceSystem({}); diff --git a/tests/work/data/tables/base_level.json b/tests/work/data/tables/base_level.json new file mode 100644 index 000000000..92e5a9397 --- /dev/null +++ b/tests/work/data/tables/base_level.json @@ -0,0 +1,19 @@ +{ + "terrain_setup": { + "config": { + "use": false + } + }, + "vegetation_setup": { + + }, + "sun_settings": { + + }, + "cluster_settings": { + + }, + "histogram_settings": { + + } +} \ No newline at end of file diff --git a/toolkit/editor/editor/ui/modules/viewport.cc b/toolkit/editor/editor/ui/modules/viewport.cc index 96da48cdf..41b06882c 100644 --- a/toolkit/editor/editor/ui/modules/viewport.cc +++ b/toolkit/editor/editor/ui/modules/viewport.cc @@ -218,7 +218,7 @@ Viewport::Render() auto view = Graphics::GraphicsServer::Instance()->GetView("mainview"); view->SetViewport(Math::rectangle(0, 0, imageSize.x, imageSize.y)); - this->focused = ImGui::IsWindowFocused(); + this->focused = ImGui::IsWindowFocused() | ImGui::IsWindowHovered(); if (this->focused) { diff --git a/toolkit/editor/editor/ui/windows/asseteditor/materialasseteditor.cc b/toolkit/editor/editor/ui/windows/asseteditor/materialasseteditor.cc index 5587c4572..1b9a149da 100644 --- a/toolkit/editor/editor/ui/windows/asseteditor/materialasseteditor.cc +++ b/toolkit/editor/editor/ui/windows/asseteditor/materialasseteditor.cc @@ -186,7 +186,32 @@ MaterialEditor(AssetEditor* assetEditor, AssetEditorItem* item) } } ImGui::SameLine(); - pressed |= ImGui::Button(Util::Format("%s###BUTTON%s", name.AsCharPtr(), name.AsCharPtr()).AsCharPtr()); + pressed |= ImGui::Button(Util::Format("%s###BUTTON%d", name.AsCharPtr(), i).AsCharPtr()); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_None)) + { + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) + { + ImGui::SetClipboardText(name.AsCharPtr()); + pressed = false; + } + else if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) + { + const char* clipboard = ImGui::GetClipboardText(); + if (clipboard != nullptr) + { + auto cmd = new CMDMaterialSetTexture; + cmd->bindlessOffset = value->bindlessOffset; + cmd->index = i; + cmd->hash = value->hashedName; + cmd->assetEditor = assetEditor; + cmd->asset = clipboard; + cmd->item = item; + cmd->oldAsset = name; + Edit::CommandManager::Execute(cmd); + } + pressed = false; + } + } if (pressed) { // TODO: Replace file dialog with asset browser view/instance @@ -204,7 +229,7 @@ MaterialEditor(AssetEditor* assetEditor, AssetEditorItem* item) cmd->item = item; cmd->oldAsset = name; Edit::CommandManager::Execute(cmd); - } + } } } } @@ -214,7 +239,7 @@ MaterialEditor(AssetEditor* assetEditor, AssetEditorItem* item) { auto& kvp = materialTemplate->values.KeyValuePairAtIndex(i); const MaterialTemplates::MaterialTemplateValue* value = kvp.Value(); - ubyte* imguiState = (ubyte*)StackAlloc(materialTemplate->bufferSize); + ubyte* imguiState = ArrayAllocStack(materialTemplate->bufferSize); ubyte* currentState = Materials::MaterialGetConstants(item->asset.material); memcpy(imguiState, currentState, materialTemplate->bufferSize); @@ -227,22 +252,22 @@ MaterialEditor(AssetEditor* assetEditor, AssetEditorItem* item) } case MaterialTemplates::MaterialTemplateValue::Type::Scalar: { - ImGui::SliderFloat(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1.0f); + ImGui::SliderFloat(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1000.0f); break; } case MaterialTemplates::MaterialTemplateValue::Type::Vec2: { - ImGui::SliderFloat2(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1.0f); + ImGui::SliderFloat2(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1000.0f); break; } case MaterialTemplates::MaterialTemplateValue::Type::Vec3: { - ImGui::SliderFloat3(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1.0f); + ImGui::SliderFloat3(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1000.0f); break; } case MaterialTemplates::MaterialTemplateValue::Type::Vec4: { - ImGui::SliderFloat4(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1.0f); + ImGui::SliderFloat4(kvp.Key(), (float*)(imguiState + value->offset), 0.0f, 1000.0f); break; } case MaterialTemplates::MaterialTemplateValue::Type::Color: @@ -280,7 +305,7 @@ MaterialEditor(AssetEditor* assetEditor, AssetEditorItem* item) ImGui::SetTooltip(kvp.Value()->desc); } - StackFree(imguiState); + ArrayFreeStack(materialTemplate->bufferSize, imguiState); } } @@ -350,7 +375,8 @@ MaterialSave(AssetEditor* assetEditor, AssetEditorItem* item) Ptr writer = IO::XmlWriter::Create(); writer->SetStream(stream); - writer->Open(); + bool isOpen = writer->Open(); + n_assert(isOpen); writer->BeginNode("Nebula"); { writer->BeginNode("Surface"); diff --git a/toolkit/editor/editor/ui/windows/resourcebrowser.cc b/toolkit/editor/editor/ui/windows/resourcebrowser.cc index 190ae40bf..2a8461156 100644 --- a/toolkit/editor/editor/ui/windows/resourcebrowser.cc +++ b/toolkit/editor/editor/ui/windows/resourcebrowser.cc @@ -93,16 +93,39 @@ ResourceBrowser::Run(SaveMode save) CoreGraphics::TextureIdLock _0(CoreGraphics::TrackedTextures[current]); selectedTex.nebulaHandle = CoreGraphics::TrackedTextures[current]; CoreGraphics::TextureDimensions dims = CoreGraphics::TextureGetDimensions(CoreGraphics::TrackedTextures[current]); - float ratio = dims.width / dims.height; + float ratio = dims.height / float(dims.width); ImVec2 remainder = ImGui::GetContentRegionAvail(); static int mip = 0, layer = 0; static bool alpha = false; + static bool range = false; + static bool red = true, green = true, blue = true, a = true; + static float minRange = 0, maxRange = 1; if (ImGui::BeginChild("Preview", ImVec2{ 0, 0 })) { ImGui::Image(&selectedTex, ImVec2{ (float)remainder.x, (float)remainder.x * ratio }); ImGui::InputInt("Mip", &mip); ImGui::InputInt("Layer", &layer); + + ImGui::Checkbox("R", &red); + ImGui::SameLine(); + ImGui::Checkbox("G", &green); + ImGui::SameLine(); + ImGui::Checkbox("B", &blue); + ImGui::SameLine(); + ImGui::Checkbox("A", &a); + ImGui::Checkbox("Alpha", &alpha); + ImGui::Checkbox("Range", &range); + if (range) + { + static float rangeMax = 200.0f; + ImGui::InputFloat("Range Max", &rangeMax); + ImGui::SliderFloat("Min", &minRange, 0.001f, rangeMax); + ImGui::SliderFloat("Max", &maxRange, 0.001f, rangeMax); + float tempMin = minRange; + minRange = Math::min(minRange, maxRange); + maxRange = Math::max(tempMin, maxRange); + } mip = Math::min(Math::max(0, mip), CoreGraphics::TextureGetNumMips(CoreGraphics::TrackedTextures[current]) - 1); layer = Math::min(Math::max(0, layer), CoreGraphics::TextureGetNumLayers(CoreGraphics::TrackedTextures[current]) - 1); ImGui::EndChild(); @@ -111,6 +134,13 @@ ResourceBrowser::Run(SaveMode save) selectedTex.layer = layer; selectedTex.mip = mip; selectedTex.useAlpha = alpha; + selectedTex.useRange = range; + selectedTex.rangeMin = minRange; + selectedTex.rangeMax = maxRange; + selectedTex.red = red; + selectedTex.green = green; + selectedTex.blue = blue; + selectedTex.alpha = a; } else { diff --git a/toolkit/editor/editor/ui/windows/scene.cc b/toolkit/editor/editor/ui/windows/scene.cc index 9c774eaac..db8103baf 100644 --- a/toolkit/editor/editor/ui/windows/scene.cc +++ b/toolkit/editor/editor/ui/windows/scene.cc @@ -182,7 +182,8 @@ Scene::DrawOutlines() else { Math::vec3 location = defaultWorld->GetComponent(gameEntity); - const Math::bbox box = Math::bbox(location, Math::vector(0.1f, 0.1f, 0.1f)); + Math::vec3 scale = defaultWorld->GetComponent(gameEntity); + const Math::bbox box = Math::bbox(location, scale); Im3d::Im3dContext::DrawOrientedBox(Math::mat4::identity, box, {1.0f, 0.30f, 0.0f, 1.0f}); } diff --git a/toolkit/editor/editorfeature/editorfeatureunit.cc b/toolkit/editor/editorfeature/editorfeatureunit.cc index 1f8ec13e2..a82e42ad7 100644 --- a/toolkit/editor/editorfeature/editorfeatureunit.cc +++ b/toolkit/editor/editorfeature/editorfeatureunit.cc @@ -12,6 +12,7 @@ #include "graphicsfeature/components/model.h" #include "graphicsfeature/components/decal.h" #include "graphicsfeature/components/lighting.h" +#include "graphicsfeature/components/gi.h" #include "dynui/im3d/im3dcontext.h" // TEMP: Move this to editor game manager @@ -22,6 +23,7 @@ #include "lighting/lightcontext.h" #include "models/modelcontext.h" #include "decals/decalcontext.h" +#include "gi/ddgicontext.h" namespace EditorFeature { @@ -155,6 +157,23 @@ EditorFeatureUnit::OnActivate() ) .Build(); + Game::ProcessorBuilder(world, "EditorGameManager.UpdateDDGIVolumeTransform"_atm) + .On("OnEndFrame") + .OnlyModified() + .RunInEditor() + .Func( + [](Game::World* world, + Game::Position const& pos, + Game::Orientation const& rot, + Game::Scale const& scale, + GraphicsFeature::DDGIVolume const& volume) + { + GI::DDGIContext::SetPosition(volume.graphicsEntityId, pos); + GI::DDGIContext::SetSize(volume.graphicsEntityId, scale); + } + ) + .Build(); + //if (!Editor::ConnectToBackend(...)) // Editor::SpawnLocalBackend(); } diff --git a/toolkit/toolkitutil/model/import/base/modelexporter.cc b/toolkit/toolkitutil/model/import/base/modelexporter.cc index bc6d12f23..e5051bc72 100644 --- a/toolkit/toolkitutil/model/import/base/modelexporter.cc +++ b/toolkit/toolkitutil/model/import/base/modelexporter.cc @@ -114,9 +114,8 @@ ModelExporter::ExportFile(const IO::URI& file) // Merge meshes based on vertex component and material Util::Array mergedMeshNodes; Util::Array mergedCharacterNodes; - Util::Array mergedGroups; Util::Array mergedMeshes; - this->scene->OptimizeGraphics(mergedMeshNodes, mergedCharacterNodes, mergedGroups, mergedMeshes); + this->scene->OptimizeGraphics(mergedMeshNodes, mergedCharacterNodes, mergedMeshes); Util::String physicsMeshExportName = String::Sprintf("msh:%s/%s_ph.nvx", this->category.AsCharPtr(), this->file.AsCharPtr()); IO::URI destinationFiles[] = @@ -136,7 +135,7 @@ ModelExporter::ExportFile(const IO::URI& file) }; // save mesh to file - if (!MeshBuilderSaver::Save(destinationFiles[DestinationFile::Mesh], mergedMeshes, mergedGroups, this->platform)) + if (!MeshBuilderSaver::Save(destinationFiles[DestinationFile::Mesh], mergedMeshes, this->platform)) { this->logger->Error("Failed to save NVX file : % s\n", destinationFiles[DestinationFile::Mesh].LocalPath().AsCharPtr()); } @@ -168,9 +167,7 @@ ModelExporter::ExportFile(const IO::URI& file) timer.Start(); // save mesh - group.SetFirstTriangleIndex(0); - group.SetNumTriangles(physicsMesh->GetNumTriangles()); - if (!MeshBuilderSaver::Save(destinationFiles[DestinationFile::Physics], { physicsMesh }, { group }, this->platform)) + if (!MeshBuilderSaver::Save(destinationFiles[DestinationFile::Physics], { physicsMesh }, this->platform)) { this->logger->Error("Failed to save physics NVX file : % s\n", destinationFiles[DestinationFile::Physics].LocalPath().AsCharPtr()); } diff --git a/toolkit/toolkitutil/model/import/base/scene.cc b/toolkit/toolkitutil/model/import/base/scene.cc index 2d4b5343a..f762f964f 100644 --- a/toolkit/toolkitutil/model/import/base/scene.cc +++ b/toolkit/toolkitutil/model/import/base/scene.cc @@ -49,7 +49,7 @@ Scene::~Scene() Caller takes ownership of the output meshes and has to delete them */ void -Scene::OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& outCharacterNodes, Util::Array& outGroups, Util::Array& outMeshes) +Scene::OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& outCharacterNodes, Util::Array& outMeshes) { // Bucket meshes based on components Util::Dictionary> nodesByComponents; @@ -66,90 +66,72 @@ Scene::OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& nodes = nodesByComponents.ValueAtIndex(i); - builder->SetComponents(nodesByComponents.KeyAtIndex(i)); - builder->SetPrimitiveTopology(this->meshes[nodes[0]->mesh.meshIndex].GetPrimitiveTopology()); + Util::Array groups; + IndexT groupId = 0; - // Sort nodes on material, this allows us to merge all meshes in sequence - std::sort(nodes.begin(), nodes.end(), [](SceneNode* lhs, SceneNode* rhs) - { - return lhs->mesh.material < rhs->mesh.material; - }); + MeshBuilder* builder = new MeshBuilder; + const Util::Array& nodes = nodesByComponents.ValueAtIndex(i); + builder->SetComponents(nodesByComponents.KeyAtIndex(i)); + builder->SetPrimitiveTopology(this->meshes[nodes[0]->mesh.meshIndex].GetPrimitiveTopology()); - IndexT triangleOffset = 0, numTriangles = 0; - SceneNode* firstNodeInRange = nullptr; - MeshBuilderGroup* group; - for (IndexT j = 0; j < nodes.Size(); j++) - { - auto meshToMerge = this->meshes[nodes[j]->mesh.meshIndex]; + // Sort nodes on material, this allows us to merge all meshes in sequence + std::sort(nodes.begin(), nodes.end(), [](SceneNode* lhs, SceneNode* rhs) + { + return lhs->mesh.material < rhs->mesh.material; + }); + + IndexT triangleOffset = 0, numTriangles = 0; + SceneNode* firstNodeInRange = nullptr; + MeshBuilderGroup* group; + for (IndexT j = 0; j < nodes.Size(); j++) + { + auto meshToMerge = this->meshes[nodes[j]->mesh.meshIndex]; - // Since we're flattening the scene, integrate the transform in the mesh - meshToMerge.Transform(nodes[j]->base.globalTransform); - nodes[j]->base.rotation = Math::quat(); - nodes[j]->base.scale = Math::vec3(1); - nodes[j]->base.translation = Math::vec3(0); + // Since we're flattening the scene, integrate the transform in the mesh + meshToMerge.Transform(nodes[j]->base.globalTransform); + nodes[j]->base.rotation = Math::quat(); + nodes[j]->base.scale = Math::vec3(1); + nodes[j]->base.translation = Math::vec3(0); - // Whenever we hit another material, we need a new primitive group - if (firstNodeInRange == nullptr - || nodes[j]->mesh.material != firstNodeInRange->mesh.material) - { - // Repoint first node in range - firstNodeInRange = nodes[j]; + // Whenever we hit another material, we need a new primitive group + if (firstNodeInRange == nullptr + || nodes[j]->mesh.material != firstNodeInRange->mesh.material) + { + // Repoint first node in range + firstNodeInRange = nodes[j]; - // Set group ID within this mesh and the index into the whole mesh resource - firstNodeInRange->mesh.groupId = groupId++; - firstNodeInRange->mesh.meshIndex = outMeshes.Size(); - firstNodeInRange->base.boundingBox.begin_extend(); + // Set group ID within this mesh and the index into the whole mesh resource + firstNodeInRange->mesh.groupId = groupId++; + firstNodeInRange->mesh.meshIndex = outMeshes.Size(); + firstNodeInRange->base.boundingBox.begin_extend(); - // This is also the output node - outMeshNodes.Append(firstNodeInRange); + // This is also the output node + outMeshNodes.Append(firstNodeInRange); - // Get a new group - group = &outGroups.Emplace(); + // Get a new group + group = &groups.Emplace(); - // Add group and set first index - triangleOffset += numTriangles; - group->SetFirstTriangleIndex(triangleOffset); - numTriangles = 0; - } + // Add group and set first index + triangleOffset += numTriangles; + group->SetFirstTriangleIndex(triangleOffset); + numTriangles = 0; + } - // Calculate the new triangle count for our current group - numTriangles += meshToMerge.GetNumTriangles(); - group->SetNumTriangles(numTriangles); + // Calculate the new triangle count for our current group + numTriangles += meshToMerge.GetNumTriangles(); + group->SetNumTriangles(numTriangles); - firstNodeInRange->base.boundingBox.extend(meshToMerge.ComputeBoundingBox()); + firstNodeInRange->base.boundingBox.extend(meshToMerge.ComputeBoundingBox()); - // Merge meshes - builder->Merge(meshToMerge); - } - outMeshes.Append(builder); - } - } - else - { - for (IndexT i = 0; i < nodesByComponents.Size(); i++) - { - const Util::Array& nodes = nodesByComponents.ValueAtIndex(i); - for (auto& node : nodes) - { - MeshBuilder* mesh = &this->meshes[node->mesh.meshIndex]; - node->mesh.groupId = groupId++; - outMeshNodes.Append(node); - outMeshes.Append(mesh); - - MeshBuilderGroup group; - group.SetFirstTriangleIndex(0); - group.SetNumTriangles(mesh->GetNumTriangles()); - outGroups.Append(group); - } + // Merge meshes + builder->Merge(meshToMerge); } + builder->SetPrimitiveGroups(groups); + outMeshes.Append(builder); } // Go through and split primitive groups if they are skins. @@ -157,15 +139,19 @@ Scene::OptimizeGraphics(Util::Array& outMeshNodes, Util::Array groups; + if (node->base.isSkin) { MeshBuilder* mesh = outMeshes[node->mesh.meshIndex]; + IndexT groupId = 0; + Util::Set joints; SizeT baseTriangle = 0; SizeT numTriangles = 0; IndexT currentGroup = node->mesh.groupId; - MeshBuilderGroup& group = outGroups[currentGroup]; + MeshBuilderGroup& group = mesh->GetPrimitiveGroups()[currentGroup]; for (IndexT j = 0; j < mesh->GetNumTriangles(); j++) { const MeshBuilderTriangle& tri = mesh->TriangleAt(j); @@ -196,7 +182,7 @@ Scene::OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& outMeshNodes, Util::Array& lookup = node->skin.jointLookup[i]; - const MeshBuilderGroup& group = outGroups[node->skin.skinFragments[i]]; + const MeshBuilderGroup& group = mesh->GetPrimitiveGroups()[node->skin.skinFragments[i]]; for (IndexT i = 0; i < group.GetNumTriangles(); i++) { const MeshBuilderTriangle& tri = mesh->TriangleAt(group.GetFirstTriangleIndex() + i); diff --git a/toolkit/toolkitutil/model/import/base/scene.h b/toolkit/toolkitutil/model/import/base/scene.h index 129d5fd30..b24089ca9 100644 --- a/toolkit/toolkitutil/model/import/base/scene.h +++ b/toolkit/toolkitutil/model/import/base/scene.h @@ -39,7 +39,7 @@ class Scene const Util::String& GetCategory() const; /// Merges the scene into a set of nodes and their corresponding primitive groups, and a set of meshes - void OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& outCharacterNodes, Util::Array& outGroups, Util::Array& outMeshes); + void OptimizeGraphics(Util::Array& outMeshNodes, Util::Array& outCharacterNodes, Util::Array& outMeshes); /// Merges physics nodes and meshes into a single mesh builder and a set of nodes void OptimizePhysics(Util::Array& outNodes, MeshBuilder*& outMesh); diff --git a/toolkit/toolkitutil/model/import/fbx/node/nfbxmeshnode.cc b/toolkit/toolkitutil/model/import/fbx/node/nfbxmeshnode.cc index c9a90ee01..adae5b6d4 100644 --- a/toolkit/toolkitutil/model/import/fbx/node/nfbxmeshnode.cc +++ b/toolkit/toolkitutil/model/import/fbx/node/nfbxmeshnode.cc @@ -178,7 +178,7 @@ NFbxMeshNode::ExtractMesh( { const ufbx_face& face = fbxMesh->faces[polygonIndex]; size_t requiredIndices = (face.num_indices - 2) * 3; - uint32_t* indices = (uint32_t*)StackAlloc(requiredIndices * sizeof(uint32)); + uint32_t* indices = ArrayAllocStack(requiredIndices); uint status = ufbx_triangulate_face(indices, requiredIndices, fbxMesh, face); n_assert_fmt(status != 0, "Triangulation failed on polygon %d", polygonIndex); for (int tri = 0; tri < requiredIndices / 3; tri++) @@ -261,7 +261,7 @@ NFbxMeshNode::ExtractMesh( } mesh.AddTriangle(meshTriangle); } - StackFree(indices); + ArrayFreeStack(requiredIndices, indices); } if (AllBits(node->mesh.meshFlags, HasNormals)) diff --git a/toolkit/toolkitutil/model/import/gltf/ngltfexporter.cc b/toolkit/toolkitutil/model/import/gltf/ngltfexporter.cc index 8277952b1..e8ee9c819 100644 --- a/toolkit/toolkitutil/model/import/gltf/ngltfexporter.cc +++ b/toolkit/toolkitutil/model/import/gltf/ngltfexporter.cc @@ -90,7 +90,7 @@ NglTFExporter::ParseScene() // Set texture attrs for normal textures TextureAttrs attrs; attrs.SetPixelFormat(TextureAttrs::BC5); - attrs.SetFlipNormalY(true); + attrs.SetFlipNormalY(false); attrs.SetMaxHeight(8192); attrs.SetMaxWidth(8192); diff --git a/toolkit/toolkitutil/model/import/gltf/ngltfmaterialexporter.cc b/toolkit/toolkitutil/model/import/gltf/ngltfmaterialexporter.cc index a69ea1320..c0a0b067c 100644 --- a/toolkit/toolkitutil/model/import/gltf/ngltfmaterialexporter.cc +++ b/toolkit/toolkitutil/model/import/gltf/ngltfmaterialexporter.cc @@ -204,7 +204,7 @@ NglTFMaterialExtractor::ExtractMaterial(SurfaceBuilder& builder, Gltf::Material } else { - builder.AddParam("emissiveTexture", "systex:black"); + builder.AddParam("emissiveTexture", "systex:white"); } if (material.occlusionTexture.index != -1) diff --git a/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc b/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc index 7d3a90602..12d18c3db 100644 --- a/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc +++ b/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc @@ -71,9 +71,9 @@ AttributeToComponentIndex(Gltf::Primitive::Attribute attribute, bool normalized) //------------------------------------------------------------------------------ /** */ -template +template Math::vec4 -ReadVertexData(void const* const buffer, const uint i) +ReadVertexData(void const* const buffer, const size_t i) { static_assert(n >= 1 && n <= 4, "You're doing it wrong!"); TYPE const* const v = (TYPE*)(buffer); @@ -84,17 +84,17 @@ ReadVertexData(void const* const buffer, const uint i) } else if constexpr (n == 2) { - uint const offset = i * n; + size_t const offset = i * n; ret = Math::vec4((float)v[offset], (float)v[offset + 1], 0.0f, 0.0f); } else if constexpr (n == 3) { - uint const offset = i * n; + size_t const offset = i * n; ret = Math::vec4((float)v[offset], (float)v[offset + 1], (float)v[offset + 2], 0.0f); } else if constexpr (n == 4) { - uint const offset = i * n; + size_t const offset = i * n; ret = Math::vec4((float)v[offset], (float)v[offset + 1], (float)v[offset + 2], (float)v[offset + 3]); } return ret; @@ -104,7 +104,7 @@ ReadVertexData(void const* const buffer, const uint i) /** */ Math::vec4 -ReadVertexData(CoreGraphics::VertexComponent::Format format, void const* const buffer, uint32_t index) +ReadVertexData(CoreGraphics::VertexComponent::Format format, void const* const buffer, size_t index) { Math::vec4 data; switch (format) @@ -277,9 +277,9 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv for (size_t j = 0; j < count; j++) { Math::vec4 data = ReadVertexData(vertexBufferAccessor.format, vbuf, j); - uint32_t idx = *((uint32_t*)(ibuf + (j * indexByteStride))); + size_t idx = *((uint32_t*)(ibuf + (j * indexByteStride))); idx &= indexMask; - MeshBuilderVertex& vtx = meshBuilder->VertexAt(idx); + MeshBuilderVertex& vtx = meshBuilder->VertexAt((IndexT)idx); vtx.SetPosition(data); } } @@ -299,7 +299,7 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv for (size_t i = 0; i < triCount * 3; i += 3) { MeshBuilderTriangle triangle; - triangle.SetVertexIndices(i, i + 1, i + 2); + triangle.SetVertexIndices((IndexT)i, (IndexT)i + 1, (IndexT)i + 2); meshBuilder->AddTriangle(triangle); } } diff --git a/toolkit/toolkitutil/model/meshutil/meshbuilder.cc b/toolkit/toolkitutil/model/meshutil/meshbuilder.cc index a6f9682fd..5e053ec0a 100644 --- a/toolkit/toolkitutil/model/meshutil/meshbuilder.cc +++ b/toolkit/toolkitutil/model/meshutil/meshbuilder.cc @@ -70,6 +70,33 @@ MeshBuilder::GetPrimitiveTopology() return this->topology; } +//------------------------------------------------------------------------------ +/** +*/ +void +MeshBuilder::SetPrimitiveGroups(const Util::Array& groups) +{ + this->groups = groups; +} + +//------------------------------------------------------------------------------ +/** +*/ +const Util::Array& +MeshBuilder::GetPrimitiveGroups() +{ + return this->groups; +} + +//------------------------------------------------------------------------------ +/** +*/ +void +MeshBuilder::ClearPrimitiveGroups() +{ + this->groups.Clear(); +} + //------------------------------------------------------------------------------ /** This method copies a triangle with its vertices from the source mesh @@ -459,6 +486,8 @@ MeshBuilder::Merge(MeshBuilder const& sourceMesh) IndexT vertIndex; SizeT triCount = sourceMesh.GetNumTriangles(); IndexT triIndex; + SizeT groupCount = sourceMesh.groups.Size(); + IndexT groupIndex; for (vertIndex = 0; vertIndex < sourceMesh.GetNumVertices(); vertIndex++) { @@ -476,6 +505,11 @@ MeshBuilder::Merge(MeshBuilder const& sourceMesh) // add triangle to mesh this->AddTriangle(tri); } + + for (groupIndex = 0; groupIndex < groupCount; groupIndex++) + { + this->groups.Append(sourceMesh.groups[groupIndex]); + } } //------------------------------------------------------------------------------ diff --git a/toolkit/toolkitutil/model/meshutil/meshbuilder.h b/toolkit/toolkitutil/model/meshutil/meshbuilder.h index 33ad2e00d..6d8a24556 100644 --- a/toolkit/toolkitutil/model/meshutil/meshbuilder.h +++ b/toolkit/toolkitutil/model/meshutil/meshbuilder.h @@ -55,6 +55,13 @@ class MeshBuilder /// get primitive topology const CoreGraphics::PrimitiveTopology::Code& GetPrimitiveTopology(); + /// Set groups + void SetPrimitiveGroups(const Util::Array& groups); + /// Get groups + const Util::Array& GetPrimitiveGroups(); + /// Clear primitive groups + void ClearPrimitiveGroups(); + /// copy triangle with its vertices, do not generate redundant vertices void CopyTriangle(const MeshBuilder& srcMesh, IndexT triIndex, Util::FixedArray& indexMap); /// compute overall bounding box @@ -96,6 +103,7 @@ class MeshBuilder CoreGraphics::PrimitiveTopology::Code topology; MeshBuilderVertex::ComponentMask componentMask; Util::Array vertices; + Util::Array groups; }; //------------------------------------------------------------------------------ diff --git a/toolkit/toolkitutil/model/meshutil/meshbuildersaver.cc b/toolkit/toolkitutil/model/meshutil/meshbuildersaver.cc index 20ced6aed..5e38df8e6 100644 --- a/toolkit/toolkitutil/model/meshutil/meshbuildersaver.cc +++ b/toolkit/toolkitutil/model/meshutil/meshbuildersaver.cc @@ -24,7 +24,7 @@ using namespace Math; /** */ bool -MeshBuilderSaver::Save(const IO::URI& uri, const Util::Array& meshes, const Util::Array& groups, Platform::Code platform) +MeshBuilderSaver::Save(const IO::URI& uri, const Util::Array& meshes, Platform::Code platform) { // make sure the target directory exists IoServer::Instance()->CreateDirectory(uri.LocalPath().ExtractDirName()); @@ -35,12 +35,11 @@ MeshBuilderSaver::Save(const IO::URI& uri, const Util::Array& mesh { ByteOrder byteOrder(ByteOrder::Host, Platform::GetPlatformByteOrder(platform)); - MeshBuilderSaver::WriteHeader(stream, meshes, groups, byteOrder); + MeshBuilderSaver::WriteHeader(stream, meshes, byteOrder); MeshBuilderSaver::WriteMeshes(stream, meshes, byteOrder); - MeshBuilderSaver::WriteGroups(stream, groups, byteOrder); + MeshBuilderSaver::WriteMeshlets(stream, meshes, byteOrder); MeshBuilderSaver::WriteVertices(stream, meshes, byteOrder); MeshBuilderSaver::WriteTriangles(stream, meshes, byteOrder); - MeshBuilderSaver::WriteMeshlets(stream, meshes, byteOrder); stream->Close(); stream = nullptr; @@ -59,12 +58,16 @@ MeshBuilderSaver::Save(const IO::URI& uri, const Util::Array& mesh /** */ void -MeshBuilderSaver::WriteHeader(const Ptr& stream, const Util::Array& meshes, const Util::Array& groups, const System::ByteOrder& byteOrder) +MeshBuilderSaver::WriteHeader(const Ptr& stream, const Util::Array& meshes, const System::ByteOrder& byteOrder) { SizeT indexDataSize = 0; SizeT vertexDataSize = 0; + SizeT meshDataSize = meshes.Size() * sizeof(Nvx3VertexRange); + SizeT meshletDataSize = 0; for (IndexT i = 0; i < meshes.Size(); i++) { + meshDataSize += meshes[i]->groups.Size() * sizeof(Nvx3Group); + // The enum is the size of the type vertexDataSize += sizeof(CoreGraphics::BaseVertex) * meshes[i]->vertices.Size(); vertexDataSize += MeshBuilderVertex::GetSize(meshes[i]->componentMask) * meshes[i]->vertices.Size(); @@ -77,14 +80,17 @@ MeshBuilderSaver::WriteHeader(const Ptr& stream, const Util::Array(NEBULA_NVX_MAGICNUMBER); + nvx3Header.meshDataOffset = sizeof(Nvx3Header); nvx3Header.numMeshes = byteOrder.Convert(meshes.Size()); - nvx3Header.numGroups = byteOrder.Convert(groups.Size()); - nvx3Header.numMeshlets = 0; - nvx3Header.indexDataSize = indexDataSize; + nvx3Header.meshletDataOffset = sizeof(Nvx3Header) + meshDataSize; + nvx3Header.numMeshlets = 0; // TODO: Add meshlet support + nvx3Header.vertexDataOffset = sizeof(Nvx3Header) + meshDataSize + meshletDataSize; nvx3Header.vertexDataSize = vertexDataSize; + nvx3Header.indexDataOffset = sizeof(Nvx3Header) + meshDataSize + meshletDataSize + vertexDataSize; + nvx3Header.indexDataSize = indexDataSize; // write header - stream->Write(&nvx3Header, sizeof(nvx3Header)); + stream->Write(&nvx3Header, sizeof(Nvx3Header)); } //------------------------------------------------------------------------------ @@ -95,6 +101,9 @@ MeshBuilderSaver::WriteMeshes(const Ptr& stream, const Util::Array groups; for (IndexT curMeshIndex = 0; curMeshIndex < meshes.Size(); curMeshIndex++) { const MeshBuilder* mesh = meshes[curMeshIndex]; @@ -108,35 +117,36 @@ MeshBuilderSaver::WriteMeshes(const Ptr& stream, const Util::Arraygroups.Size(); + stream->Write(&nvx3VertexRange, sizeof(Nvx3VertexRange)); - stream->Write(&nvx3VertexRange, sizeof(nvx3VertexRange)); + for (IndexT curGroupIndex = 0; curGroupIndex < mesh->groups.Size(); curGroupIndex++) + { + const MeshBuilderGroup& group = mesh->groups[curGroupIndex]; + int firstTriangle = group.GetFirstTriangleIndex(); + int numTriangles = group.GetNumTriangles(); + + Nvx3Group nvx3Group; + nvx3Group.firstIndex = byteOrder.Convert(firstTriangle * 3); + nvx3Group.numIndices = byteOrder.Convert(numTriangles * 3); + nvx3Group.primType = PrimitiveTopology::TriangleList; + + // TODO: Add support for meshlets + nvx3Group.firstMeshlet = 0; + nvx3Group.numMeshlets = 0; + groups.Append(nvx3Group); + } + groupByteOffset += mesh->groups.Size() * sizeof(Nvx3Group); indexByteOffset += mesh->triangles.Size() * 3 * IndexType::SizeOf(nvx3VertexRange.indexType); vertexByteOffset += baseVertexDataSize + attributesVertexDataSize; } -} -//------------------------------------------------------------------------------ -/** -*/ -void -MeshBuilderSaver::WriteGroups(const Ptr& stream, const Util::Array& groups, const System::ByteOrder& byteOrder) -{ + // Write groups after all meshes for (IndexT groupIndex = 0; groupIndex < groups.Size(); groupIndex++) { - const MeshBuilderGroup& curGroup = groups[groupIndex]; - int firstTriangle = curGroup.GetFirstTriangleIndex(); - int numTriangles = curGroup.GetNumTriangles(); - - Nvx3Group nvx3Group; - nvx3Group.firstIndex = byteOrder.Convert(firstTriangle * 3); - nvx3Group.numIndices = byteOrder.Convert(numTriangles * 3); - nvx3Group.primType = PrimitiveTopology::TriangleList; - nvx3Group.firstMeshlet = 0; - nvx3Group.numMeshlets = 0; // TODO - - // write group to stream - stream->Write(&nvx3Group, sizeof(nvx3Group)); + stream->Write(&groups[groupIndex], sizeof(Nvx3Group)); } } @@ -178,7 +188,7 @@ MeshBuilderSaver::WriteVertices(const Ptr& stream, const Util::Array 0.0f ? 0x80 : 0x7E; + outVtx.tangent.w = vtx.attributes.normal.sign < 0.0f ? 0x80 : 0x7F; }; switch (layout) { diff --git a/toolkit/toolkitutil/model/meshutil/meshbuildersaver.h b/toolkit/toolkitutil/model/meshutil/meshbuildersaver.h index 9b3a17366..465b7e5b6 100644 --- a/toolkit/toolkitutil/model/meshutil/meshbuildersaver.h +++ b/toolkit/toolkitutil/model/meshutil/meshbuildersaver.h @@ -19,15 +19,13 @@ class MeshBuilderSaver { public: /// save nvx3 file - static bool Save(const IO::URI& uri, const Util::Array& meshes, const Util::Array& groups, Platform::Code platform); + static bool Save(const IO::URI& uri, const Util::Array& meshes, Platform::Code platform); private: /// write header to stream using nvx3 - static void WriteHeader(const Ptr& stream, const Util::Array& meshes, const Util::Array& groups, const System::ByteOrder& byteOrder); + static void WriteHeader(const Ptr& stream, const Util::Array& meshes, const System::ByteOrder& byteOrder); /// Write meshes static void WriteMeshes(const Ptr& stream, const Util::Array& meshes, const System::ByteOrder& byteOrder); - /// write groups to stream in nvx3 - static void WriteGroups(const Ptr& stream, const Util::Array& groups, const System::ByteOrder& byteOrder); /// write the vertices to stream static void WriteVertices(const Ptr& stream, const Util::Array& meshes, const System::ByteOrder& byteOrder);