commit 0a4552a1a45aa10b6c481b0f8d41ab0238a51982 Author: Dynamitos Date: Mon Apr 6 09:55:59 2026 +0200 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5a214ad --- /dev/null +++ b/.clang-format @@ -0,0 +1,297 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: true +BinPackLongBracedList: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BracedInitializerIndentWidth: -1 +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTemplateCloser: false +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 140 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +EnumTrailingComma: Leave +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +OneLineFormatOffRegex: '' +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: + Enabled: true + IgnoreCase: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterOperatorKeyword: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterNot: false + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5b79a54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/ +.cache/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..48e72de --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Seele"] + path = Seele + url = https://git.hoegler.org/Dynamitos/Seele diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6f4251c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch", + "type": "lldb", + "request": "launch", + "program": "${workspaceRoot}/build/FluidSimulation", + "args": [], + "cwd": "${workspaceRoot}/build" + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7c608d7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.24) + +set(CMAKE_CXX_STANDARD 23) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET 26.0) +endif() + +set(CMAKE_TOOLCHAIN_FILE + ${CMAKE_CURRENT_SOURCE_DIR}/Seele/external/vcpkg/scripts/buildsystems/vcpkg.cmake) + +project(FluidSimulation) + +set(CMAKE_DEBUG_POSTFIX "") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR}) + +add_subdirectory(Seele/) + +add_executable(FluidSimulation "") +target_link_libraries(FluidSimulation PRIVATE Engine) +target_include_directories(FluidSimulation PRIVATE src/) +add_subdirectory(src/) + +add_custom_target(FluidSimulationAll ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/res $ + COMMAND + DEPENDS FluidSimulation) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..87c6a95 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,122 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 19, + "patch": 0 + }, + "configurePresets": [ + { + "name": "release", + "displayName": "Release (no ASan)", + "description": "Release build without sanitizers", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + }, + { + "name": "release-asan", + "displayName": "Release (ASan)", + "description": "Release build with Address Sanitizer", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_C_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + }, + { + "name": "debug", + "displayName": "Debug (no ASan)", + "description": "Debug build without sanitizers", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + }, + { + "name": "debug-mac", + "displayName": "Debug Mac (no ASan)", + "description": "Debug Mac build without sanitizers", + "binaryDir": "${sourceDir}/build", + "generator": "Xcode", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + }, + { + "name": "debug-mac-asan", + "displayName": "Debug Mac (ASan)", + "description": "Debug Mac build with Address Sanitizer", + "binaryDir": "${sourceDir}/build", + "generator": "Xcode", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_C_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + }, + { + "name": "debug-asan", + "displayName": "Debug (ASan)", + "description": "Debug build with Address Sanitizer", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_C_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", + "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address", + "CMAKE_INSTALL_PREFIX": "C:/Program Files/Seele" + } + } + ], + "buildPresets": [ + { + "name": "release", + "displayName": "Release (no ASan)", + "configurePreset": "release", + "jobs": 0 + }, + { + "name": "release-asan", + "displayName": "Release (ASan)", + "configurePreset": "release-asan", + "jobs": 0 + }, + { + "name": "debug", + "displayName": "Debug (no ASan)", + "configurePreset": "debug", + "jobs": 0 + }, + { + "name": "debug-mac", + "displayName": "Debug Mac (no ASan)", + "configurePreset": "debug-mac", + "jobs": 0 + }, + { + "name": "debug-mac-asan", + "displayName": "Debug Mac (ASan)", + "configurePreset": "debug-mac-asan", + "jobs": 0 + }, + { + "name": "debug-asan", + "displayName": "Debug (ASan)", + "configurePreset": "debug-asan", + "jobs": 0 + } + ] +} diff --git a/Seele b/Seele new file mode 160000 index 0000000..7271fe8 --- /dev/null +++ b/Seele @@ -0,0 +1 @@ +Subproject commit 7271fe8a0b9814ab42efb069ce090f62a0b39de7 diff --git a/res/shaders/Advect.slang b/res/shaders/Advect.slang new file mode 100644 index 0000000..3b2467e --- /dev/null +++ b/res/shaders/Advect.slang @@ -0,0 +1,55 @@ +import FluidGridData; + +struct Params +{ + // read-write + FluidGridData density; + // read-only + FluidGridData density0; + // read-only + FluidGridData velocityX; + // read-only + FluidGridData velocityY; + // read-only + FluidGridData velocityZ; + float dt; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void advect(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData density = params.density; + FluidGridData density0 = params.density0; + FluidGridData velocityX = params.velocityX; + FluidGridData velocityY = params.velocityY; + FluidGridData velocityZ = params.velocityZ; + float dt = params.dt; + + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + + float dt0x = dt * (gridSize.x - 2); + float dt0y = dt * (gridSize.y - 2); + float dt0z = dt * (gridSize.z - 2); + + float3 pos = float3(x - dt0x * velocityX[x, y, z], y - dt0y * velocityY[x, y, z], z - dt0z * velocityZ[x, y, z]); + pos.x = clamp(pos.x, 0.5f, gridSize.x - 1.5f); + pos.y = clamp(pos.y, 0.5f, gridSize.y - 1.5f); + pos.z = clamp(pos.z, 0.5f, gridSize.z - 1.5f); + + int3 i0 = int3(pos); + int3 i1 = i0 + int3(1, 1, 1); + + float3 s1 = pos - i0; + float3 s0 = 1 - s1; + + density[x, y, z] = + s0.x * (s0.y * (s0.z * density0[i0.x, i0.y, i0.z] + s1.z * density0[i0.x, i0.y, i1.z]) + + s1.y * (s0.z * density0[i0.x, i1.y, i0.z] + s1.z * density0[i0.x, i1.y, i1.z])) + + s1.x * (s0.y * (s0.z * density0[i1.x, i0.y, i0.z] + s1.z * density0[i1.x, i0.y, i1.z]) + + s1.y * (s0.z * density0[i1.x, i1.y, i0.z] + s1.z * density0[i1.x, i1.y, i1.z])); +} \ No newline at end of file diff --git a/res/shaders/Divergence.slang b/res/shaders/Divergence.slang new file mode 100644 index 0000000..d1c5c69 --- /dev/null +++ b/res/shaders/Divergence.slang @@ -0,0 +1,35 @@ +import FluidGridData; + +struct Params +{ + // read-only + FluidGridData velocityX; + // read-only + FluidGridData velocityY; + // read-only + FluidGridData velocityZ; + // read-write + FluidGridData divergence; + // read-write + FluidGridData pressure; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void computeDivergence(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData velocityX = params.velocityX; + FluidGridData velocityY = params.velocityY; + FluidGridData velocityZ = params.velocityZ; + FluidGridData divergence = params.divergence; + FluidGridData pressure = params.pressure; + + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + + divergence[x, y, z] = -0.5f * (velocityX[x + 1, y, z] - velocityX[x - 1, y, z] + velocityY[x, y + 1, z] - velocityY[x, y - 1, z] + velocityZ[x, y, z + 1] - velocityZ[x, y, z - 1]) / gridSize.x; + pressure[x, y, z] = 0; +} \ No newline at end of file diff --git a/res/shaders/FluidGridData.slang b/res/shaders/FluidGridData.slang new file mode 100644 index 0000000..ba6f868 --- /dev/null +++ b/res/shaders/FluidGridData.slang @@ -0,0 +1,15 @@ +static const uint3 gridSize = uint3(128, 128, 128); +struct FluidGridData +{ + RWStructuredBuffer dataGrid; + __subscript(uint3 index) -> T + { + get { return dataGrid[index.x + index.y * gridSize.x + index.z * gridSize.x * gridSize.y]; } + set { dataGrid[index.x + index.y * gridSize.x + index.z * gridSize.x * gridSize.y] = newValue; } + } + __subscript(uint x, uint y, uint z) -> T + { + get { return dataGrid[x + y * gridSize.x + z * gridSize.x * gridSize.y]; } + set { dataGrid[x + y * gridSize.x + z * gridSize.x * gridSize.y] = newValue; } + } +}; diff --git a/res/shaders/LinearSolve.slang b/res/shaders/LinearSolve.slang new file mode 100644 index 0000000..c8dd9ef --- /dev/null +++ b/res/shaders/LinearSolve.slang @@ -0,0 +1,32 @@ +import FluidGridData; + +struct Params +{ + // read-write + FluidGridData next; + // read-only + FluidGridData current; + // read-only + FluidGridData grid0; + float a; + float c; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void linearSolve(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData next = params.next; + FluidGridData current = params.current; + FluidGridData grid0 = params.grid0; + float a = params.a; + float cRecip = 1.0f / params.c; + + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + + next[x, y, z] = (grid0[x, y, z] + a * (current[x + 1, y, z] + current[x - 1, y, z] + current[x, y + 1, z] + current[x, y - 1, z] + current[x, y, z + 1] + current[x, y, z - 1])) * cRecip; +} \ No newline at end of file diff --git a/res/shaders/Project.slang b/res/shaders/Project.slang new file mode 100644 index 0000000..78cdfb2 --- /dev/null +++ b/res/shaders/Project.slang @@ -0,0 +1,33 @@ +import FluidGridData; + +struct Params +{ + // read-write + FluidGridData velocityX; + // read-write + FluidGridData velocityY; + // read-write + FluidGridData velocityZ; + // read-only + FluidGridData pressure; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void project(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData velocityX = params.velocityX; + FluidGridData velocityY = params.velocityY; + FluidGridData velocityZ = params.velocityZ; + FluidGridData pressure = params.pressure; + + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + int z = dispatchThreadID.z + 1; + if(x >= gridSize.x - 1 || y >= gridSize.y - 1 || z >= gridSize.z - 1) return; + + velocityX[x, y, z] -= 0.5f * (pressure[x + 1, y, z] - pressure[x - 1, y, z]) * gridSize.x; + velocityY[x, y, z] -= 0.5f * (pressure[x, y + 1, z] - pressure[x, y - 1, z]) * gridSize.y; + velocityZ[x, y, z] -= 0.5f * (pressure[x, y, z + 1] - pressure[x, y, z - 1]) * gridSize.z; +} \ No newline at end of file diff --git a/res/shaders/Render.slang b/res/shaders/Render.slang new file mode 100644 index 0000000..fb22bd9 --- /dev/null +++ b/res/shaders/Render.slang @@ -0,0 +1,32 @@ +import FluidGridData; + +struct Params +{ + FluidGridData density; +}; +ParameterBlock params; + +struct VertexOut +{ + float4 position : SV_Position; + float2 uv : UV; +}; + +[shader("vertex")] +VertexOut vertexMain(uint vertexId : SV_VertexID) +{ + VertexOut output; + output.uv = float2((vertexId << 1) & 2, vertexId & 2); + output.position = float4(output.uv * 2.0f - 1.0f, 0, 1); + return output; +} + +[shader("pixel")] +float4 fragmentMain(VertexOut input) : SV_Target +{ + FluidGridData density = params.density; + int x = int(input.uv.x * (gridSize.x - 2) + 1); + int y = int(input.uv.y * (gridSize.y - 2) + 1); + float d = density[x, y, 0]; + return float4(d, d, d, 1); +} \ No newline at end of file diff --git a/res/shaders/SetBound.slang b/res/shaders/SetBound.slang new file mode 100644 index 0000000..0184bbc --- /dev/null +++ b/res/shaders/SetBound.slang @@ -0,0 +1,71 @@ +import FluidGridData; + +struct Params +{ + int b; + // read-write + FluidGridData grid; +}; +ParameterBlock params; + +[shader("compute")] +[numthreads(32, 8, 1)] +void setBound(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + int b = params.b; + FluidGridData grid = params.grid; + int x = dispatchThreadID.x + 1; + int y = dispatchThreadID.y + 1; + if(x >= gridSize.x - 1 || y >= gridSize.y - 1) return; + + grid[0, x, y] = b == 1 ? grid[1, x, y] : -grid[1, x, y]; + grid[gridSize.x - 1, x, y] = b == 1 ? grid[gridSize.x - 2, x, y] : -grid[gridSize.x - 2, x, y]; + + grid[x, 0, y] = b == 2 ? grid[x, 1, y] : -grid[x, 1, y]; + grid[x, gridSize.y - 1, y] = b == 2 ? grid[x, gridSize.y - 2, y] : -grid[x, gridSize.y - 2, y]; + + grid[x, y, 0] = b == 3 ? grid[x, y, 1] : -grid[x, y, 1]; + grid[x, y, gridSize.z - 1] = b == 3 ? grid[x, y, gridSize.z - 2] : -grid[x, y, gridSize.z - 2]; +} + +[shader("compute")] +[numthreads(128, 1, 1)] +void setBoundEdges(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData grid = params.grid; + int x = dispatchThreadID.x + 1; + if(x >= gridSize.x - 1) return; + + grid[x, 0, 0] = 0.5f * (grid[x, 1, 0] + grid[x, 0, 1]); + grid[x, gridSize.y - 1, 0] = 0.5f * (grid[x, gridSize.y - 2, 0] + grid[x, gridSize.y - 1, 1]); + grid[x, 0, gridSize.z - 1] = 0.5f * (grid[x, 1, gridSize.z - 1] + grid[x, 0, gridSize.z - 2]); + grid[x, gridSize.y - 1, gridSize.z - 1] = 0.5f * (grid[x, gridSize.y - 2, gridSize.z - 1] + grid[x, gridSize.y - 1, gridSize.z - 2]); + + grid[0, x, 0] = 0.5f * (grid[1, x, 0] + grid[0, x, 1]); + grid[0, x, gridSize.z - 1] = 0.5f * (grid[1, x, gridSize.z - 1] + grid[0, x, gridSize.z - 2]); + grid[gridSize.x - 1, x, 0] = 0.5f * (grid[gridSize.x - 2, x, 0] + grid[gridSize.x - 1, x, 1]); + grid[gridSize.x - 1, x, gridSize.z - 1] = 0.5f * (grid[gridSize.x - 2, x, gridSize.z - 1] + grid[gridSize.x - 1, x, gridSize.z - 2]); + + grid[0, 0, x] = 0.5f * (grid[1, 0, x] + grid[0, 1, x]); + grid[0, gridSize.y - 1, x] = 0.5f * (grid[1, gridSize.y - 1, x] + grid[0, gridSize.y - 2, x]); + grid[gridSize.x - 1, 0, x] = 0.5f * (grid[gridSize.x - 2, 0, x] + grid[gridSize.x - 1, 1, x]); + grid[gridSize.x - 1, gridSize.y - 1, x] = 0.5f * (grid[gridSize.x - 2, gridSize.y - 1, x] + grid[gridSize.x - 1, gridSize.y - 2, x]); +} + +[shader("compute")] +[numthreads(8, 1, 1)] +void setBoundCorners(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + FluidGridData grid = params.grid; + uint threadIdx = dispatchThreadID.x; + + uint x = ((threadIdx & 1) == 0) ? 0 : (gridSize.x - 1); + uint y = ((threadIdx & 2) == 0) ? 0 : (gridSize.y - 1); + uint z = ((threadIdx & 4) == 0) ? 0 : (gridSize.z - 1); + + uint nx = (x == 0) ? 1 : (gridSize.x - 2); + uint ny = (y == 0) ? 1 : (gridSize.y - 2); + uint nz = (z == 0) ? 1 : (gridSize.z - 2); + + grid[x, y, z] = 0.33f * (grid[nx, y, z] + grid[x, ny, z] + grid[x, y, nz]); +} \ No newline at end of file diff --git a/res/shaders/Update.slang b/res/shaders/Update.slang new file mode 100644 index 0000000..4e5edc9 --- /dev/null +++ b/res/shaders/Update.slang @@ -0,0 +1,108 @@ +import FluidGridData; + +void linearSolve(int b, FluidGridData grid, const FluidGridData grid0, float a, float c, int iterations) +{ + float cRecip = 1.0f / c; + for(int k = 0; k < iterations; k++) + { + for(int z = 1; z < gridSize.z - 1; z++) + { + for(int y = 1; y < gridSize.y - 1; y++) + { + for(int x = 1; x < gridSize.x - 1; x++) + { + grid[x, y, z] = (grid0[x, y, z] + a * (grid[x + 1, y, z] + grid[x - 1, y, z] + grid[x, y + 1, z] + grid[x, y - 1, z] + grid[x, y, z + 1] + grid[x, y, z - 1])) * cRecip; + } + } + } + setBound(b, grid); + } +} + +void diffuse(int b, FluidGridData grid, const FluidGridData grid0, float diff, float dt, int iterations) +{ + float a = dt * diff * (gridSize.x - 2) * (gridSize.y - 2) * (gridSize.z - 2); + linearSolve(b, grid, grid0, a, 1 + 6 * a, iterations); +} + +void advect(int b, FluidGridData density, FluidGridData density0, FluidGridData velocityX, FluidGridData velocityY, FluidGridData velocityZ, float dt) +{ + float dt0x = dt * (gridSize.x - 2); + float dt0y = dt * (gridSize.y - 2); + float dt0z = dt * (gridSize.z - 2); + + for(int z = 1; z < gridSize.z - 1; z++) + { + for(int y = 1; y < gridSize.y - 1; y++) + { + for(int x = 1; x < gridSize.x - 1; x++) + { + float3 pos = float3(x - dt0x * velocityX[x, y, z], y - dt0y * velocityY[x, y, z], z - dt0z * velocityZ[x, y, z]); + pos.x = clamp(pos.x, 0.5f, gridSize.x - 1.5f); + pos.y = clamp(pos.y, 0.5f, gridSize.y - 1.5f); + pos.z = clamp(pos.z, 0.5f, gridSize.z - 1.5f); + + int3 i0 = int3(pos); + int3 i1 = i0 + int3(1, 1, 1); + + float3 s1 = pos - i0; + float3 s0 = 1 - s1; + + density[x, y, z] = + s0.x * (s0.y * (s0.z * density0[i0.x, i0.y, i0.z] + s1.z * density0[i0.x, i0.y, i1.z]) + + s1.y * (s0.z * density0[i0.x, i1.y, i0.z] + s1.z * density0[i0.x, i1.y, i1.z])) + + s1.x * (s0.y * (s0.z * density0[i1.x, i0.y, i0.z] + s1.z * density0[i1.x, i0.y, i1.z]) + + s1.y * (s0.z * density0[i1.x, i1.y, i0.z] + s1.z * density0[i1.x, i1.y, i1.z])); + } + } + } + setBound(b, density); +} + +void project(FluidGridData velocityX, FluidGridData velocityY, FluidGridData velocityZ, FluidGridData pressure, FluidGridData divergence, int iter) +{ + for(int z = 1; z < gridSize.z - 1; z++) + { + for(int y = 1; y < gridSize.y - 1; y++) + { + for(int x = 1; x < gridSize.x - 1; x++) + { + divergence[x, y, z] = -0.5f * (velocityX[x + 1, y, z] - velocityX[x - 1, y, z] + velocityY[x, y + 1, z] - velocityY[x, y - 1, z] + velocityZ[x, y, z + 1] - velocityZ[x, y, z - 1]) / gridSize.x; + pressure[x, y, z] = 0; + } + } + } + setBound(0, divergence); + setBound(0, pressure); + linearSolve(0, pressure, divergence, 1, 6, iter); + for(int z = 1; z < gridSize.z - 1; z++) + { + for(int y = 1; y < gridSize.y - 1; y++) + { + for(int x = 1; x < gridSize.x - 1; x++) + { + velocityX[x, y, z] -= 0.5f * (pressure[x + 1, y, z] - pressure[x - 1, y, z]) * gridSize.x; + velocityY[x, y, z] -= 0.5f * (pressure[x, y + 1, z] - pressure[x, y - 1, z]) * gridSize.y; + velocityZ[x, y, z] -= 0.5f * (pressure[x, y, z + 1] - pressure[x, y, z - 1]) * gridSize.z; + } + } + } + setBound(1, velocityX); + setBound(2, velocityY); + setBound(3, velocityZ); +} + +[shader("compute")] +void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID) +{ + diffuse(1, pComputeParams.velocityX0, pComputeParams.velocityX, pComputeParams.viscosity, pComputeParams.dt, 20); + diffuse(2, pComputeParams.velocityY0, pComputeParams.velocityY, pComputeParams.viscosity, pComputeParams.dt, 20); + diffuse(3, pComputeParams.velocityZ0, pComputeParams.velocityZ, pComputeParams.viscosity, pComputeParams.dt, 20); + project(pComputeParams.velocityX0, pComputeParams.velocityY0, pComputeParams.velocityZ0, pComputeParams.scratch, pComputeParams.density, 20); + advect(1, pComputeParams.velocityX, pComputeParams.velocityX0, pComputeParams.velocityX0, pComputeParams.velocityY0, pComputeParams.velocityZ0, pComputeParams.dt); + advect(2, pComputeParams.velocityY, pComputeParams.velocityY0, pComputeParams.velocityX0, pComputeParams.velocityY0, pComputeParams.velocityZ0, pComputeParams.dt); + advect(3, pComputeParams.velocityZ, pComputeParams.velocityZ0, pComputeParams.velocityX0, pComputeParams.velocityY0, pComputeParams.velocityZ0, pComputeParams.dt); + + diffuse(0, pComputeParams.scratch, pComputeParams.density, pComputeParams.diffusion, pComputeParams.dt, 20); + advect(0, pComputeParams.density, pComputeParams.scratch, pComputeParams.velocityX0, pComputeParams.velocityY0, pComputeParams.velocityZ0, pComputeParams.dt); +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..9a27d0f --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,9 @@ +target_sources(FluidSimulation + PRIVATE + SimulationComputePass.h + SimulationComputePass.cpp + FluidRenderPass.h + FluidRenderPass.cpp + FluidSimulationView.h + FluidSimulationView.cpp + main.cpp) diff --git a/src/FluidRenderPass.cpp b/src/FluidRenderPass.cpp new file mode 100644 index 0000000..f7ed291 --- /dev/null +++ b/src/FluidRenderPass.cpp @@ -0,0 +1,122 @@ +#include "FluidRenderPass.h" +#include "Graphics/Enums.h" +#include "Graphics/Graphics.h" +#include "Graphics/Initializer.h" +#include "Graphics/RenderTarget.h" +#include "Graphics/Shader.h" + +using namespace Seele; + +FluidRenderPass::FluidRenderPass(Seele::Gfx::PGraphics graphics) : RenderPass(graphics) { + pipelineLayout = graphics->createPipelineLayout("FluidPipelineLayout"); + descriptorLayout = graphics->createDescriptorLayout("params"); + descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "density", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + descriptorLayout->create(); + pipelineLayout->addDescriptorLayout(descriptorLayout); + + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "Fluid", + .modules = {"Render"}, + .entryPoints = {{"vertexMain", "Render"}, {"fragmentMain", "Render"}}, + .rootSignature = pipelineLayout, + .dumpIntermediate = true, + }); + pipelineLayout->create(); + vertexShader = graphics->createVertexShader({0}); + fragmentShader = graphics->createFragmentShader({1}); +} + +FluidRenderPass::~FluidRenderPass() {} + +void FluidRenderPass::beginFrame(const Seele::Component::Camera &camera, const Seele::Component::Transform &transform) { + updateViewParameters(camera, transform); + descriptorSet = descriptorLayout->allocateDescriptorSet(); + descriptorSet->updateBuffer("density", 0, densityBuffer); + descriptorSet->writeChanges(); +} + +void FluidRenderPass::render() { + graphics->beginRenderPass(renderPass); + graphics->endRenderPass(); +} + +void FluidRenderPass::endFrame() {} + +void FluidRenderPass::publishOutputs() { + depthTexture = graphics->createTexture2D(TextureCreateInfo{ + .format = Gfx::SE_FORMAT_D32_SFLOAT, + .width = viewport->getWidth(), + .height = viewport->getHeight(), + .name = "FluidDepth", + .usage = Gfx::SE_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + }); +} + +void FluidRenderPass::createRenderPass() { + densityBuffer = resources->requestBuffer("DENSITY"); + colorAttachment = Gfx::RenderTargetAttachment(viewport->getOwner()->getBackBuffer()->getDefaultView(), Gfx::SE_IMAGE_LAYOUT_UNDEFINED, + Gfx::SE_IMAGE_LAYOUT_PRESENT_SRC_KHR, Gfx::SE_ATTACHMENT_LOAD_OP_CLEAR, + Gfx::SE_ATTACHMENT_STORE_OP_STORE); + depthAttachment = Gfx::RenderTargetAttachment(depthTexture->getDefaultView(), Gfx::SE_IMAGE_LAYOUT_UNDEFINED, + Gfx::SE_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, Gfx::SE_ATTACHMENT_LOAD_OP_CLEAR, + Gfx::SE_ATTACHMENT_STORE_OP_DONT_CARE); + depthAttachment.clear.depthStencil.depth = 0.0f; + Gfx::RenderTargetLayout layout = Gfx::RenderTargetLayout{ + .colorAttachments = {colorAttachment}, + .depthAttachment = depthAttachment, + }; + Array dependency = { + { + .srcSubpass = ~0U, + .dstSubpass = 0, + .srcStage = Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .dstStage = Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .srcAccess = Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccess = Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + }, + { + .srcSubpass = 0, + .dstSubpass = ~0U, + .srcStage = Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .dstStage = Gfx::SE_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | Gfx::SE_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + Gfx::SE_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, + .srcAccess = Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccess = Gfx::SE_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + Gfx::SE_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + }, + }; + renderPass = graphics->createRenderPass(layout, dependency, viewport->getRenderArea(), "FluidRenderPass"); + pipeline = graphics->createGraphicsPipeline(Gfx::LegacyPipelineCreateInfo{ + .vertexShader = vertexShader, + .fragmentShader = fragmentShader, + .renderPass = renderPass, + .pipelineLayout = pipelineLayout, + .multisampleState = + { + .samples = Gfx::SE_SAMPLE_COUNT_1_BIT, + }, + .rasterizationState = + { + .cullMode = Gfx::SE_CULL_MODE_BACK_BIT, + }, + .colorBlend = + { + .attachmentCount = 1, + .blendAttachments = + { + Gfx::ColorBlendState::BlendAttachment{ + .blendEnable = true, + }, + }, + }, + }); +} \ No newline at end of file diff --git a/src/FluidRenderPass.h b/src/FluidRenderPass.h new file mode 100644 index 0000000..f16301c --- /dev/null +++ b/src/FluidRenderPass.h @@ -0,0 +1,34 @@ +#pragma once +#include "Component/Transform.h" +#include "Graphics/Descriptor.h" +#include "Graphics/Initializer.h" +#include "Graphics/RenderPass/RenderPass.h" +#include "MinimalEngine.h" + +class FluidRenderPass : public Seele::RenderPass { +public: + FluidRenderPass(Seele::Gfx::PGraphics graphics); + virtual ~FluidRenderPass() override; + + virtual void beginFrame(const Seele::Component::Camera &camera, const Seele::Component::Transform &transform) override; + virtual void render() override; + virtual void endFrame() override; + + virtual void publishOutputs() override; + virtual void createRenderPass() override; + +private: + Seele::Gfx::RenderTargetAttachment colorAttachment; + Seele::Gfx::RenderTargetAttachment depthAttachment; + Seele::Gfx::OTexture2D depthTexture; + + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::ODescriptorSet descriptorSet; + Seele::Gfx::OVertexShader vertexShader; + Seele::Gfx::OFragmentShader fragmentShader; + Seele::Gfx::PGraphicsPipeline pipeline; + + Seele::Gfx::PShaderBuffer densityBuffer; +}; +DEFINE_REF(FluidRenderPass) \ No newline at end of file diff --git a/src/FluidSimulationView.cpp b/src/FluidSimulationView.cpp new file mode 100644 index 0000000..a31dc22 --- /dev/null +++ b/src/FluidSimulationView.cpp @@ -0,0 +1,36 @@ +#include "FluidSimulationView.h" +#include "FluidRenderPass.h" +#include "SimulationComputePass.h" + +using namespace Seele; + +FluidSimulationView::FluidSimulationView(Gfx::PGraphics graphics, Seele::PWindow window, const Seele::ViewportCreateInfo &createInfo) + : View(graphics, window, createInfo, "Fluid Simulation View") { + camera = Component::Camera{}; + transform = Component::Transform{ + .transform = Math::Transform({0.0f, 0.0f, -5.0f}), + }; + renderGraph.addPass(new SimulationComputePass(graphics)); + renderGraph.addPass(new FluidRenderPass(graphics)); + renderGraph.setViewport(viewport); + renderGraph.createRenderPass(); +} + +FluidSimulationView::~FluidSimulationView() {} + +void FluidSimulationView::beginUpdate() {} + +void FluidSimulationView::update() {} + +void FluidSimulationView::commitUpdate() {} + +void FluidSimulationView::prepareRender() {} + +void FluidSimulationView::render() { renderGraph.render(camera, transform); } + +void FluidSimulationView::applyArea(Seele::URect area) {} +void FluidSimulationView::keyCallback(Seele::KeyCode code, Seele::InputAction action, Seele::KeyModifier modifier) {} +void FluidSimulationView::mouseMoveCallback(double xPos, double yPos) {} +void FluidSimulationView::mouseButtonCallback(Seele::MouseButton button, Seele::InputAction action, Seele::KeyModifier modifier) {} +void FluidSimulationView::scrollCallback(double xOffset, double yOffset) {} +void FluidSimulationView::fileCallback(int count, const char **paths) {} \ No newline at end of file diff --git a/src/FluidSimulationView.h b/src/FluidSimulationView.h new file mode 100644 index 0000000..0fe25a8 --- /dev/null +++ b/src/FluidSimulationView.h @@ -0,0 +1,31 @@ +#pragma once +#include "Graphics/Enums.h" +#include "Graphics/Initializer.h" +#include "Graphics/RenderPass/RenderGraph.h" +#include "Window/View.h" + +class FluidSimulationView : public Seele::View { +public: + FluidSimulationView(Seele::Gfx::PGraphics graphics, Seele::PWindow window, const Seele::ViewportCreateInfo &createInfo); + ~FluidSimulationView() override; + + virtual void beginUpdate() override; + virtual void update() override; + virtual void commitUpdate() override; + + virtual void prepareRender() override; + virtual void render() override; + +protected: + virtual void applyArea(Seele::URect area) override; + virtual void keyCallback(Seele::KeyCode code, Seele::InputAction action, Seele::KeyModifier modifier) override; + virtual void mouseMoveCallback(double xPos, double yPos) override; + virtual void mouseButtonCallback(Seele::MouseButton button, Seele::InputAction action, Seele::KeyModifier modifier) override; + virtual void scrollCallback(double xOffset, double yOffset) override; + virtual void fileCallback(int count, const char **paths) override; + +private: + Seele::Component::Camera camera; + Seele::Component::Transform transform; + Seele::RenderGraph renderGraph; +}; \ No newline at end of file diff --git a/src/SimulationComputePass.cpp b/src/SimulationComputePass.cpp new file mode 100644 index 0000000..1e9a645 --- /dev/null +++ b/src/SimulationComputePass.cpp @@ -0,0 +1,487 @@ +#include "SimulationComputePass.h" +#include "Graphics/Command.h" +#include "Graphics/Descriptor.h" +#include "Graphics/Enums.h" +#include "Graphics/Graphics.h" +#include "Graphics/Initializer.h" +#include "Graphics/Shader.h" + +using namespace Seele; + +#define SWAP(a, b) \ + { \ + auto temp = std::move(a); \ + a = std::move(b); \ + b = std::move(temp); \ + } + +SimulationComputePass::SimulationComputePass(Seele::Gfx::PGraphics graphics) : RenderPass(graphics) { + { + setBounds.pipelineLayout = graphics->createPipelineLayout("SetBoundsPipelineLayout"); + setBounds.descriptorLayout = graphics->createDescriptorLayout("params"); + setBounds.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "b", + .uniformLength = sizeof(int), + }); + setBounds.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "grid", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + setBounds.descriptorLayout->create(); + setBounds.pipelineLayout->addDescriptorLayout(setBounds.descriptorLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "SetBound", + .modules = {"SetBound"}, + .entryPoints = {{"setBound", "SetBound"}, {"setBoundEdges", "SetBound"}, {"setBoundCorners", "SetBound"}}, + .rootSignature = setBounds.pipelineLayout, + }); + setBounds.pipelineLayout->create(); + setBounds.shader = graphics->createComputeShader({0}); + setBounds.shaderEdges = graphics->createComputeShader({1}); + setBounds.shaderCorners = graphics->createComputeShader({2}); + setBounds.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = setBounds.shader, + .pipelineLayout = setBounds.pipelineLayout, + }); + setBounds.pipelineEdges = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = setBounds.shaderEdges, + .pipelineLayout = setBounds.pipelineLayout, + }); + setBounds.pipelineCorners = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = setBounds.shaderCorners, + .pipelineLayout = setBounds.pipelineLayout, + }); + } + { + linearSolve.pipelineLayout = graphics->createPipelineLayout("LinearSolvePipelineLayout"); + linearSolve.descriptorLayout = graphics->createDescriptorLayout("params"); + linearSolve.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "a", + .uniformLength = sizeof(float), + }); + linearSolve.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "c", + .uniformLength = sizeof(float), + }); + linearSolve.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "next", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + linearSolve.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "current", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + linearSolve.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "grid0", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + linearSolve.descriptorLayout->create(); + linearSolve.pipelineLayout->addDescriptorLayout(linearSolve.descriptorLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "LinearSolve", + .modules = {"LinearSolve"}, + .entryPoints = {{"linearSolve", "LinearSolve"}}, + .rootSignature = linearSolve.pipelineLayout, + }); + linearSolve.pipelineLayout->create(); + linearSolve.shader = graphics->createComputeShader({0}); + linearSolve.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = linearSolve.shader, + .pipelineLayout = linearSolve.pipelineLayout, + }); + } + { + computeDivergence.pipelineLayout = graphics->createPipelineLayout("ComputeDivergencePipelineLayout"); + computeDivergence.descriptorLayout = graphics->createDescriptorLayout("params"); + computeDivergence.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityX", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + computeDivergence.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityY", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + computeDivergence.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityZ", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + computeDivergence.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "divergence", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + computeDivergence.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "pressure", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + computeDivergence.descriptorLayout->create(); + computeDivergence.pipelineLayout->addDescriptorLayout(computeDivergence.descriptorLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "Divergence", + .modules = {"Divergence"}, + .entryPoints = {{"computeDivergence", "Divergence"}}, + .rootSignature = computeDivergence.pipelineLayout, + }); + computeDivergence.pipelineLayout->create(); + + computeDivergence.shader = graphics->createComputeShader({0}); + computeDivergence.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = computeDivergence.shader, + .pipelineLayout = computeDivergence.pipelineLayout, + }); + } + { + project.pipelineLayout = graphics->createPipelineLayout("ProjectPipelineLayout"); + project.descriptorLayout = graphics->createDescriptorLayout("params"); + project.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityX", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + project.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityY", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + project.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityZ", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + project.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "pressure", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + project.descriptorLayout->create(); + project.pipelineLayout->addDescriptorLayout(project.descriptorLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "Project", + .modules = {"Project"}, + .entryPoints = {{"project", "Project"}}, + .rootSignature = project.pipelineLayout, + }); + project.pipelineLayout->create(); + project.shader = graphics->createComputeShader({0}); + project.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = project.shader, + .pipelineLayout = project.pipelineLayout, + }); + } + { + advect.pipelineLayout = graphics->createPipelineLayout("AdvectPipelineLayout"); + advect.descriptorLayout = graphics->createDescriptorLayout("params"); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "density", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "density0", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityX", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityY", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "velocityZ", + .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, + }); + advect.descriptorLayout->addDescriptorBinding(Gfx::DescriptorBinding{ + .name = "dt", + .uniformLength = sizeof(float), + }); + advect.descriptorLayout->create(); + advect.pipelineLayout->addDescriptorLayout(advect.descriptorLayout); + graphics->beginShaderCompilation(ShaderCompilationInfo{ + .name = "Advect", + .modules = {"Advect"}, + .entryPoints = {{"advect", "Advect"}}, + .rootSignature = advect.pipelineLayout, + }); + advect.pipelineLayout->create(); + + advect.shader = graphics->createComputeShader({0}); + advect.pipeline = graphics->createComputePipeline(Gfx::ComputePipelineCreateInfo{ + .computeShader = advect.shader, + .pipelineLayout = advect.pipelineLayout, + }); + } + + uint64_t bufferSize = gridSize * gridSize * gridSize * sizeof(float); + densityBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "DensityBuffer", + }); + scratchBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "Density0Buffer", + }); + velocityXNextBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityXNextBuffer", + }); + velocityYNextBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityYNextBuffer", + }); + velocityZNextBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityZNextBuffer", + }); + velocityXBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityXBuffer", + }); + velocityYBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityYBuffer", + }); + velocityZBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityZBuffer", + }); + velocityX0Buffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityX0Buffer", + }); + velocityY0Buffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityY0Buffer", + }); + velocityZ0Buffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ + .sourceData = + { + .size = bufferSize, + }, + .name = "VelocityZ0Buffer", + }); +} + +SimulationComputePass::~SimulationComputePass() {} + +void SimulationComputePass::beginFrame(const Seele::Component::Camera &camera, const Seele::Component::Transform &transform) { + setBounds.descriptorLayout->reset(); + linearSolve.descriptorLayout->reset(); + computeDivergence.descriptorLayout->reset(); + project.descriptorLayout->reset(); + advect.descriptorLayout->reset(); +} + +void SimulationComputePass::render() { + for (uint iteration = 0; iteration < 4; ++iteration) { + float a = dt * viscosity * (gridSize - 2) * (gridSize - 2) * (gridSize - 2); + float c = 1 + 6 * a; + linearSolvePass(velocityXNextBuffer, velocityXBuffer, velocityX0Buffer, a, c); + linearSolvePass(velocityYNextBuffer, velocityYBuffer, velocityY0Buffer, a, c); + linearSolvePass(velocityZNextBuffer, velocityZBuffer, velocityZ0Buffer, a, c); + + setBoundsPass(velocityXNextBuffer, 1); + setBoundsPass(velocityYNextBuffer, 2); + setBoundsPass(velocityZNextBuffer, 3); + + SWAP(velocityXBuffer, velocityXNextBuffer); + SWAP(velocityYBuffer, velocityYNextBuffer); + SWAP(velocityZBuffer, velocityZNextBuffer); + } + + SWAP(velocityX0Buffer, velocityXBuffer); + SWAP(velocityY0Buffer, velocityYBuffer); + SWAP(velocityZ0Buffer, velocityZBuffer); + + Gfx::PShaderBuffer divergence = velocityXBuffer; + Gfx::PShaderBuffer pressureCurrent = velocityYBuffer; + Gfx::PShaderBuffer pressureNext = velocityZBuffer; + divergencePass(divergence, pressureCurrent, velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer); + + for (int iteration = 0; iteration < 4; ++iteration) { + linearSolvePass(pressureNext, pressureCurrent, divergence, 1, 6); + + setBoundsPass(pressureNext, 0); + + // swap pressure buffers + SWAP(pressureCurrent, pressureNext); + } + + projectPass(velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer, pressureCurrent); + + setBoundsPass(velocityX0Buffer, 1); + setBoundsPass(velocityY0Buffer, 2); + setBoundsPass(velocityZ0Buffer, 3); + + advectPass(velocityXBuffer, velocityX0Buffer, velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer, dt); + advectPass(velocityYBuffer, velocityY0Buffer, velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer, dt); + advectPass(velocityZBuffer, velocityZ0Buffer, velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer, dt); + + setBoundsPass(velocityXBuffer, 1); + setBoundsPass(velocityYBuffer, 2); + setBoundsPass(velocityZBuffer, 3); + + // swap velocity buffers + SWAP(velocityX0Buffer, velocityXBuffer); + SWAP(velocityY0Buffer, velocityYBuffer); + SWAP(velocityZ0Buffer, velocityZBuffer); + + // diffuse density + for (int iteration = 0; iteration < 4; ++iteration) { + float a = dt * diffusion * (gridSize - 2) * (gridSize - 2) * (gridSize - 2); + float c = 1 + 6 * a; + linearSolvePass(scratchBuffer, densityBuffer, densityBuffer, a, c); + setBoundsPass(scratchBuffer, 0); + } + advectPass(densityBuffer, scratchBuffer, velocityX0Buffer, velocityY0Buffer, velocityZ0Buffer, dt); + setBoundsPass(densityBuffer, 0); +} + +void SimulationComputePass::endFrame() {} + +void SimulationComputePass::publishOutputs() { resources->registerBufferOutput("DENSITY", densityBuffer); } + +void SimulationComputePass::createRenderPass() {} + +void SimulationComputePass::setBoundsPass(Gfx::PShaderBuffer grid, int b) { + Gfx::ODescriptorSet bounds = setBounds.descriptorLayout->allocateDescriptorSet(); + bounds->updateConstants("b", 0, &b); + bounds->updateBuffer("grid", 0, grid); + bounds->writeChanges(); + Gfx::OComputeCommand boundsCommand = graphics->createComputeCommand(); + boundsCommand->bindPipeline(setBounds.pipeline); + boundsCommand->bindDescriptor(bounds); + boundsCommand->dispatch(gridSize / setBounds.threadGroupSize.x, gridSize / setBounds.threadGroupSize.y, 1); + graphics->executeCommands(std::move(boundsCommand)); + + grid->pipelineBarrier(Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + Gfx::OComputeCommand edgesCommand = graphics->createComputeCommand(); + edgesCommand->bindPipeline(setBounds.pipelineEdges); + edgesCommand->bindDescriptor(bounds); + edgesCommand->dispatch(gridSize / setBounds.threadGroupSize.x, 1, 1); + graphics->executeCommands(std::move(edgesCommand)); + + grid->pipelineBarrier(Gfx::SE_ACCESS_SHADER_READ_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_WRITE_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + + Gfx::OComputeCommand cornersCommand = graphics->createComputeCommand(); + cornersCommand->bindPipeline(setBounds.pipelineCorners); + cornersCommand->bindDescriptor(bounds); + cornersCommand->dispatch(1, 1, 1); + graphics->executeCommands(std::move(cornersCommand)); + + grid->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void SimulationComputePass::linearSolvePass(Gfx::PShaderBuffer current, Gfx::PShaderBuffer next, Gfx::PShaderBuffer grid0, float a, + float c) { + Gfx::ODescriptorSet solve = linearSolve.descriptorLayout->allocateDescriptorSet(); + solve->updateBuffer("grid0", 0, grid0); + solve->updateBuffer("current", 0, current); + solve->updateBuffer("next", 0, next); + solve->updateConstants("a", 0, &a); + solve->updateConstants("c", 0, &c); + solve->writeChanges(); + Gfx::OComputeCommand solveCommand = graphics->createComputeCommand(); + solveCommand->bindPipeline(linearSolve.pipeline); + solveCommand->bindDescriptor(solve); + solveCommand->dispatch(gridSize / linearSolve.threadGroupSize.x, gridSize / linearSolve.threadGroupSize.y, + gridSize / linearSolve.threadGroupSize.z); + graphics->executeCommands(std::move(solveCommand)); + + next->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} +void SimulationComputePass::divergencePass(Seele::Gfx::PShaderBuffer divergence, Seele::Gfx::PShaderBuffer pressure, + Seele::Gfx::PShaderBuffer velocityX, Seele::Gfx::PShaderBuffer velocityY, + Seele::Gfx::PShaderBuffer velocityZ) { + Gfx::ODescriptorSet divergenceSet = computeDivergence.descriptorLayout->allocateDescriptorSet(); + divergenceSet->updateBuffer("velocityX", 0, velocityX); + divergenceSet->updateBuffer("velocityY", 0, velocityY); + divergenceSet->updateBuffer("velocityZ", 0, velocityZ); + divergenceSet->updateBuffer("divergence", 0, divergence); + divergenceSet->updateBuffer("pressure", 0, pressure); + divergenceSet->writeChanges(); + Gfx::OComputeCommand divergenceCommand = graphics->createComputeCommand(); + divergenceCommand->bindPipeline(computeDivergence.pipeline); + divergenceCommand->bindDescriptor(divergenceSet); + divergenceCommand->dispatch(gridSize / computeDivergence.threadGroupSize.x, gridSize / computeDivergence.threadGroupSize.y, + gridSize / computeDivergence.threadGroupSize.z); + graphics->executeCommands(std::move(divergenceCommand)); + // divergence is now in velocityXBuffer, pressure is in velocityYBuffer + divergence->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + pressure->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void SimulationComputePass::projectPass(Seele::Gfx::PShaderBuffer velocityX, Seele::Gfx::PShaderBuffer velocityY, + Seele::Gfx::PShaderBuffer velocityZ, Seele::Gfx::PShaderBuffer pressure) { + Gfx::ODescriptorSet projectSet = project.descriptorLayout->allocateDescriptorSet(); + projectSet->updateBuffer("velocityX", 0, velocityX); + projectSet->updateBuffer("velocityY", 0, velocityY); + projectSet->updateBuffer("velocityZ", 0, velocityZ); + projectSet->updateBuffer("pressure", 0, pressure); + projectSet->writeChanges(); + Gfx::OComputeCommand projectCommand = graphics->createComputeCommand(); + projectCommand->bindPipeline(project.pipeline); + projectCommand->bindDescriptor(projectSet); + projectCommand->dispatch(gridSize / project.threadGroupSize.x, gridSize / project.threadGroupSize.y, + gridSize / project.threadGroupSize.z); + graphics->executeCommands(std::move(projectCommand)); + + velocityX->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + velocityY->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); + velocityZ->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void SimulationComputePass::advectPass(Gfx::PShaderBuffer quantity, Gfx::PShaderBuffer quantity0, Gfx::PShaderBuffer velocityX, + Gfx::PShaderBuffer velocityY, Gfx::PShaderBuffer velocityZ, float dt) { + Gfx::ODescriptorSet advectSet = this->advect.descriptorLayout->allocateDescriptorSet(); + advectSet->updateBuffer("density", 0, quantity); + advectSet->updateBuffer("density0", 0, quantity0); + advectSet->updateBuffer("velocityX", 0, velocityX); + advectSet->updateBuffer("velocityY", 0, velocityY); + advectSet->updateBuffer("velocityZ", 0, velocityZ); + advectSet->updateConstants("dt", 0, &dt); + advectSet->writeChanges(); + Gfx::OComputeCommand advectCommand = graphics->createComputeCommand(); + advectCommand->bindPipeline(advect.pipeline); + advectCommand->bindDescriptor(advectSet); + advectCommand->dispatch(gridSize / advect.threadGroupSize.x, gridSize / advect.threadGroupSize.y, gridSize / advect.threadGroupSize.z); + graphics->executeCommands(std::move(advectCommand)); + + quantity->pipelineBarrier(Gfx::SE_ACCESS_SHADER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT, Gfx::SE_ACCESS_SHADER_READ_BIT, + Gfx::SE_PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} diff --git a/src/SimulationComputePass.h b/src/SimulationComputePass.h new file mode 100644 index 0000000..d05b875 --- /dev/null +++ b/src/SimulationComputePass.h @@ -0,0 +1,85 @@ +#pragma once +#include "Graphics/Buffer.h" +#include "Graphics/Descriptor.h" +#include "Graphics/Initializer.h" +#include "Graphics/RenderPass/RenderPass.h" +#include "Graphics/Resources.h" +#include + +class SimulationComputePass : public Seele::RenderPass { +public: + SimulationComputePass(Seele::Gfx::PGraphics graphics); + virtual ~SimulationComputePass() override; + virtual void beginFrame(const Seele::Component::Camera &camera, const Seele::Component::Transform &transform) override; + virtual void render() override; + virtual void endFrame() override; + virtual void publishOutputs() override; + virtual void createRenderPass() override; + +private: + void setBoundsPass(Seele::Gfx::PShaderBuffer buffer, int b); + void linearSolvePass(Seele::Gfx::PShaderBuffer next, Seele::Gfx::PShaderBuffer current, Seele::Gfx::PShaderBuffer grid0, float a, float c); + void divergencePass(Seele::Gfx::PShaderBuffer divergence, Seele::Gfx::PShaderBuffer pressure, Seele::Gfx::PShaderBuffer velocityX, Seele::Gfx::PShaderBuffer velocityY, Seele::Gfx::PShaderBuffer velocityZ); + void projectPass(Seele::Gfx::PShaderBuffer velocityX, Seele::Gfx::PShaderBuffer velocityY, Seele::Gfx::PShaderBuffer velocityZ, Seele::Gfx::PShaderBuffer pressure); + void advectPass(Seele::Gfx::PShaderBuffer quantity, Seele::Gfx::PShaderBuffer quantity0, Seele::Gfx::PShaderBuffer velocityX, Seele::Gfx::PShaderBuffer velocityY, Seele::Gfx::PShaderBuffer velocityZ, float dt); + float viscosity = 0.001f; + float diffusion = 0.0001f; + float dt = 0.1f; + constexpr static uint gridSize = 128; + struct SetBounds + { + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::OComputeShader shader; + Seele::Gfx::PComputePipeline pipeline; + Seele::Gfx::OComputeShader shaderEdges; + Seele::Gfx::PComputePipeline pipelineEdges; + Seele::Gfx::OComputeShader shaderCorners; + Seele::Gfx::PComputePipeline pipelineCorners; + constexpr static Seele::UVector threadGroupSize = {32, 8, 1}; + } setBounds; + struct LinearSolve + { + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::OComputeShader shader; + Seele::Gfx::PComputePipeline pipeline; + constexpr static Seele::UVector threadGroupSize = {32, 8, 1}; + } linearSolve; + struct ComputeDivergence + { + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::OComputeShader shader; + Seele::Gfx::PComputePipeline pipeline; + constexpr static Seele::UVector threadGroupSize = {32, 8, 1}; + } computeDivergence; + struct Project + { + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::OComputeShader shader; + Seele::Gfx::PComputePipeline pipeline; + constexpr static Seele::UVector threadGroupSize = {32, 8, 1}; + } project; + struct Advect + { + Seele::Gfx::OPipelineLayout pipelineLayout; + Seele::Gfx::ODescriptorLayout descriptorLayout; + Seele::Gfx::OComputeShader shader; + Seele::Gfx::PComputePipeline pipeline; + constexpr static Seele::UVector threadGroupSize = {32, 8, 1}; + } advect; + Seele::Gfx::OShaderBuffer densityBuffer; + Seele::Gfx::OShaderBuffer scratchBuffer; + Seele::Gfx::OShaderBuffer velocityXNextBuffer; + Seele::Gfx::OShaderBuffer velocityYNextBuffer; + Seele::Gfx::OShaderBuffer velocityZNextBuffer; + Seele::Gfx::OShaderBuffer velocityXBuffer; + Seele::Gfx::OShaderBuffer velocityYBuffer; + Seele::Gfx::OShaderBuffer velocityZBuffer; + Seele::Gfx::OShaderBuffer velocityX0Buffer; + Seele::Gfx::OShaderBuffer velocityY0Buffer; + Seele::Gfx::OShaderBuffer velocityZ0Buffer; +}; +DEFINE_REF(SimulationComputePass) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..5c7be73 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,34 @@ +#include "FluidSimulationView.h" +#include "Graphics/Initializer.h" +#include "Graphics/Vulkan/Graphics.h" +#include "Window/WindowManager.h" + +using namespace Seele; + +int main() { + Gfx::OGraphics graphics = new Vulkan::Graphics(); + graphics->init(GraphicsInitializer()); + OWindowManager windowManager = new WindowManager(); + auto window = windowManager->addWindow(graphics, WindowCreateInfo{ + .title = "Seele Fluid Simulation", + .width = 1280, + .height = 720, + }); + auto view = new FluidSimulationView(graphics, window, + ViewportCreateInfo{ + .dimensions = + { + .size = + { + 1280, + 720, + }, + }, + }); + view->setFocused(); + window->show(); + while(!window->shouldClose()) { + windowManager->render(); + } + return 0; +} diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..c467f19 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,26 @@ +{ + "dependencies": [ + { + "name": "vulkan" + }, + "vulkan-loader", + { + "name": "vulkan-memory-allocator" + }, + "assimp", + "stb", + "entt", + "freetype", + "glfw3", + "glm", + "ktx", + "nlohmann-json", + "fmt", + "gtest", + "lunasvg", + "harfbuzz", + "shader-slang", + "metis", + "meshoptimizer" + ] +} \ No newline at end of file