public void PostProcess(VoxelStorageContext storageContext, RenderDrawContext drawContext, ProcessedVoxelVolume data) { if (Math.Max(Math.Max(ClipMapResolution.X, ClipMapResolution.Y), ClipMapResolution.Z) < 32) { return; } if (FragmentsBuffer is null) { return; } var context = drawContext.RenderContext; if (ClearBuffer is null) { ClearBuffer = new ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "ClearBuffer" }; BufferToTexture = new ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "BufferToTextureEffect" }; BufferToTextureColumns = new ComputeEffect.ComputeEffectShader(context) { ShaderSourceName = "BufferToTextureColumnsEffect" }; } bool VoxelsAreIndependent = true; List <VoxelAttribute> IndirectVoxels = new List <VoxelAttribute>(); List <VoxelAttribute> TempVoxels = new List <VoxelAttribute>(); ShaderSourceCollection Indirect = new ShaderSourceCollection(); ShaderSourceCollection Temp = new ShaderSourceCollection(); // Assign sample indices and check whether voxels can be calculated independently int sampleIndex = 0; foreach (var attr in data.Attributes) { attr.Attribute.LocalSamplerID = sampleIndex; VoxelsAreIndependent &= !attr.Attribute.RequiresColumns(); sampleIndex++; } // Populate ShaderSourceCollections and temp lists foreach (var attr in data.Attributes) { if (attr.Stage != VoxelizationStage.Post) { continue; } if (attr.Output) { Indirect.Add(attr.Attribute.GetVoxelizationShader()); IndirectVoxels.Add(attr.Attribute); } else { Temp.Add(attr.Attribute.GetVoxelizationShader()); TempVoxels.Add(attr.Attribute); } } var BufferWriter = VoxelsAreIndependent ? BufferToTexture : BufferToTextureColumns; for (int i = 0; i < IndirectVoxels.Count; i++) { var attr = IndirectVoxels[i]; attr.UpdateVoxelizationLayout($"AttributesIndirect[{i}]"); } for (int i = 0; i < TempVoxels.Count; i++) { var attr = TempVoxels[i]; attr.UpdateVoxelizationLayout($"AttributesTemp[{i}]"); } foreach (var attr in data.Attributes) { attr.Attribute.ApplyVoxelizationParameters(BufferWriter.Parameters); } int processYSize = VoxelsAreIndependent ? (int)ClipMapResolution.Y : 1; processYSize *= (UpdatesPerFrame == UpdateMethods.SingleClipmap) ? 1 : ClipMapCount; BufferWriter.ThreadGroupCounts = VoxelsAreIndependent ? new Int3(32, 32, 32) : new Int3(32, 1, 32); BufferWriter.ThreadNumbers = new Int3((int)ClipMapResolution.X / BufferWriter.ThreadGroupCounts.X, processYSize / BufferWriter.ThreadGroupCounts.Y, (int)ClipMapResolution.Z / BufferWriter.ThreadGroupCounts.Z); BufferWriter.Parameters.Set(BufferToTextureKeys.VoxelFragments, FragmentsBuffer); BufferWriter.Parameters.Set(BufferToTextureKeys.clipMapResolution, ClipMapResolution); BufferWriter.Parameters.Set(BufferToTextureKeys.storageUints, storageUints); BufferWriter.Parameters.Set(BufferToTextureKeys.clipOffset, (uint)(UpdatesPerFrame == UpdateMethods.SingleClipmap ? ClipMapCurrent : 0)); // Modifiers are stored within attributes, yet need to be able to query their results. // Ideally a stage stream could resolve this, however due to the lack of pointers, there would be a // cyclic dependency of AttributesList -> Attribute -> Modifier -> AttributesList -> ... // // So instead the results will be stored within a second array that only contains float4s. Unfortunately the only // way to iterate through the AttributesList is by foreach, which makes it difficult to access the results array // (AttributeLocalSamples) by index. So instead it's just all done through this macro... string IndirectReadAndStoreMacro = ""; string IndirectStoreMacro = ""; for (int i = 0; i < Temp.Count; i++) { string iStr = i.ToString(); string sampleIndexStr = TempVoxels[i].LocalSamplerID.ToString(); IndirectReadAndStoreMacro += $"AttributesTemp[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {TempVoxels[i].BufferOffset}, uint2({TempVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" + $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n\n"; IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesTemp[{iStr}].SampleLocal();\n"; } for (int i = 0; i < Indirect.Count; i++) { string iStr = i.ToString(); string sampleIndexStr = IndirectVoxels[i].LocalSamplerID.ToString(); IndirectReadAndStoreMacro += $"AttributesIndirect[{iStr}].InitializeFromBuffer(VoxelFragments, VoxelFragmentsIndex + {IndirectVoxels[i].BufferOffset}, uint2({IndirectVoxels[i].BufferOffset} + initialVoxelFragmentsIndex, yStride));\n" + $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n\n"; IndirectStoreMacro += $"streams.LocalSample[{sampleIndexStr}] = AttributesIndirect[{iStr}].SampleLocal();\n"; } BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesIndirect, Indirect); BufferWriter.Parameters.Set(BufferToTextureKeys.AttributesTemp, Temp); BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectReadAndStoreMacro, IndirectReadAndStoreMacro); BufferWriter.Parameters.Set(BufferToTextureKeys.IndirectStoreMacro, IndirectStoreMacro); BufferWriter.Draw(drawContext); ClearBuffer.Parameters.Set(ClearBufferKeys.buffer, FragmentsBuffer); if (UpdatesPerFrame != UpdateMethods.SingleClipmap) { // Clear all ClearBuffer.ThreadNumbers = new Int3(1024, 1, 1); ClearBuffer.ThreadGroupCounts = new Int3(FragmentsBuffer.ElementCount / 1024, 1, 1); ClearBuffer.Parameters.Set(ClearBufferKeys.offset, 0); } else { // Clear next clipmap buffer ClearBuffer.ThreadNumbers = new Int3(1024, 1, 1); ClearBuffer.ThreadGroupCounts = new Int3((int)(ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints) / 1024, 1, 1); ClearBuffer.Parameters.Set(ClearBufferKeys.offset, (int)(((ClipMapCurrent + 1) % ClipMapCount) * ClipMapResolution.X * ClipMapResolution.Y * ClipMapResolution.Z * storageUints)); } ClearBuffer.Draw(drawContext); }
public void PostProcess(RenderDrawContext drawContext, ShaderSource[] mipmapShaders) { if (mipmapShaders.Length != LayoutSize) { return; } if (VoxelMipmapSimple is null) { VoxelMipmapSimple = new ComputeEffect.ComputeEffectShader(drawContext.RenderContext) { ShaderSourceName = "Voxel2x2x2MipmapEffect" }; } if (VoxelMipmapSimpleGroups is null || VoxelMipmapSimpleGroups.Length != LayoutSize || VoxelMipmapSimpleGroups[0].Length != TempMipMaps.Length) { if (VoxelMipmapSimpleGroups != null) { for (int axis = 0; axis < LayoutSize; axis++) { if (VoxelMipmapSimpleGroups[axis] != null) { foreach (var shader in VoxelMipmapSimpleGroups[axis]) { shader.Dispose(); } } } } VoxelMipmapSimpleGroups = new ComputeEffect.ComputeEffectShader[LayoutSize][]; for (int axis = 0; axis < LayoutSize; axis++) { VoxelMipmapSimpleGroups[axis] = new ComputeEffect.ComputeEffectShader[TempMipMaps.Length]; for (int i = 0; i < VoxelMipmapSimpleGroups[axis].Length; i++) { VoxelMipmapSimpleGroups[axis][i] = new ComputeEffect.ComputeEffectShader(drawContext.RenderContext) { ShaderSourceName = "Voxel2x2x2MipmapEffect" }; } } } int offsetIndex = 0; // Mipmap detailed clipmaps into less detailed ones Vector3 totalResolution = ClipMapResolution * new Vector3(1, LayoutSize, 1); if (DownsampleFinerClipMaps) { for (int i = 0; i < ClipMapCount - 1; i++) { Vector3 Offset = MippingOffset[offsetIndex]; VoxelMipmapSimple.ThreadNumbers = new Int3(8); VoxelMipmapSimple.ThreadGroupCounts = (Int3)(ClipMapResolution / 2.0f / (Vector3)VoxelMipmapSimple.ThreadNumbers); for (int axis = 0; axis < LayoutSize; axis++) { VoxelMipmapSimple.Parameters.Set(Voxel2x2x2MipmapKeys.ReadTex, ClipMaps); VoxelMipmapSimple.Parameters.Set(Voxel2x2x2MipmapKeys.WriteTex, TempMipMaps[0]); VoxelMipmapSimple.Parameters.Set(Voxel2x2x2MipmapKeys.ReadOffset, -(Vector3.Mod(Offset, new Vector3(2))) + new Vector3(0, (int)totalResolution.Y * i + (int)ClipMapResolution.Y * axis, 0)); VoxelMipmapSimple.Parameters.Set(Voxel2x2x2MipmapKeys.WriteOffset, new Vector3(0, ClipMapResolution.Y / 2 * axis, 0)); VoxelMipmapSimple.Parameters.Set(Voxel2x2x2MipmapKeys.mipmapper, mipmapShaders[axis]); VoxelMipmapSimple.Draw(drawContext); } Offset -= Vector3.Mod(Offset, new Vector3(2)); // Copy each axis, ignoring the top and bottom plane for (int axis = 0; axis < LayoutSize; axis++) { int axisOffset = axis * (int)ClipMapResolution.Y; Int3 CopySize = new Int3((int)ClipMapResolution.X / 2 - 2, (int)ClipMapResolution.Y / 2 - 2, (int)ClipMapResolution.Z / 2 - 2); Int3 DstMinBound = new Int3((int)ClipMapResolution.X / 4 + (int)Offset.X / 2 + 1, (int)totalResolution.Y * (i + 1) + axisOffset + (int)ClipMapResolution.Y / 4 + 1 + (int)Offset.Y / 2, (int)ClipMapResolution.Z / 4 + (int)Offset.Z / 2 + 1); Int3 DstMaxBound = DstMinBound + CopySize; DstMaxBound = Int3.Min(DstMaxBound, new Int3((int)totalResolution.X, (int)totalResolution.Y * (i + 2), (int)totalResolution.Z)); DstMinBound = Int3.Min(DstMinBound, new Int3((int)totalResolution.X, (int)totalResolution.Y * (i + 2), (int)totalResolution.Z)); DstMaxBound = Int3.Max(DstMaxBound, new Int3(0, (int)totalResolution.Y * (i + 1), 0)); DstMinBound = Int3.Max(DstMinBound, new Int3(0, (int)totalResolution.Y * (i + 1), 0)); Int3 SizeBound = DstMaxBound - DstMinBound; Int3 SrcMinBound = new Int3(1, axisOffset / 2 + 1, 1); Int3 SrcMaxBound = SrcMinBound + SizeBound; if (SizeBound.X > 0 && SizeBound.Y > 0 && SizeBound.Z > 0) { drawContext.CommandList.CopyRegion(TempMipMaps[0], 0, new ResourceRegion ( SrcMinBound.X, SrcMinBound.Y, SrcMinBound.Z, SrcMaxBound.X, SrcMaxBound.Y, SrcMaxBound.Z ), ClipMaps, 0, DstMinBound.X, DstMinBound.Y, DstMinBound.Z); } } offsetIndex++; } } Vector3 resolution = ClipMapResolution; offsetIndex = ClipMapCount - 1; // Mipmaps for the largest clipmap for (int i = 0; i < TempMipMaps.Length - 1; i++) { Vector3 Offset = MippingOffset[offsetIndex]; resolution /= 2; Vector3 threadNums = Vector3.Min(resolution, new Vector3(8)); for (int axis = 0; axis < LayoutSize; axis++) { var mipmapShader = VoxelMipmapSimpleGroups[axis][i]; mipmapShader.ThreadNumbers = (Int3)threadNums; mipmapShader.ThreadGroupCounts = (Int3)(resolution / threadNums); if (i == 0) { mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.ReadTex, ClipMaps); mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.ReadOffset, -Offset + new Vector3(0, (int)ClipMapResolution.Y * LayoutSize * (ClipMapCount - 1) + (int)ClipMapResolution.Y * axis, 0)); mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.WriteOffset, new Vector3(0, resolution.Y * axis, 0)); } else { mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.ReadTex, TempMipMaps[i - 1]); mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.ReadOffset, -Offset + new Vector3(0, resolution.Y * axis * 2, 0)); mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.WriteOffset, new Vector3(0, resolution.Y * axis, 0)); } mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.WriteTex, TempMipMaps[i]); mipmapShader.Parameters.Set(Voxel2x2x2MipmapKeys.mipmapper, mipmapShaders[axis]); mipmapShader.Draw(drawContext); } // NOTE: Don't seem to be able to read and write to the same texture, even if the views point to different mipmaps drawContext.CommandList.CopyRegion(TempMipMaps[i], 0, null, MipMaps, i); offsetIndex++; } Array.Copy(PerMapOffsetScale, PerMapOffsetScaleCurrent, PerMapOffsetScale.Length); }