Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

namespace UnityEngine.Rendering.HighDefinition
{
//Do not change these numbers!!
//Its not a full power of 2 because the last light slot is reserved.
internal enum FPTLMaxLightSizes
{
Low = 31,
High = 63
}

/// <summary>
/// Project-wide shader configuration options.
/// </summary>
Expand Down Expand Up @@ -36,6 +44,13 @@ public enum ShaderOptions

/// <summary>Support to apply a global mip bias on all texture samplers of HDRP.</summary>
GlobalMipBias = 1,

/// <summary>
/// Maximum number of lights for a fine pruned light tile. This number can only be the prespecified possibilities in FPTLMaxLightSizes
/// Lower count will mean some memory savings.
/// Note: For any rendering bigger than 4k (in native) it is recommended to use Low count per tile, to avoid possible artifacts.
/// </summary>
FPTLMaxLightCount = FPTLMaxLightSizes.High
};

// Note: #define can't be use in include file in C# so we chose this way to configure both C# and hlsl
Expand Down Expand Up @@ -73,5 +88,8 @@ public class ShaderConfig
/// <summary>Indicates whether to support application of global mip bias on all texture samplers of hdrp.</summary>
///<seealso cref="ShaderOptions.GlobalMipBias"/>
public static bool s_GlobalMipBias = (int)ShaderOptions.GlobalMipBias != 0;
/// <summary>Indicates the maximum number of lights available for Fine Prunning Tile Lighting.</summary>
/// <seealso cref="ShaderOptions.FPTLMaxLightCount"/>
public static int FPTLMaxLightCount = (int)ShaderOptions.FPTLMaxLightCount;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define SHADEROPTIONS_AREA_LIGHTS (1)
#define SHADEROPTIONS_BARN_DOOR (0)
#define SHADEROPTIONS_GLOBAL_MIP_BIAS (1)
#define SHADEROPTIONS_FPTLMAX_LIGHT_COUNT (63)


#endif
3 changes: 3 additions & 0 deletions com.unity.render-pipelines.high-definition/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### Added
- Added a SG node to get the main directional light direction.
- Added public API to edit materials from script at runtime.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be here?

- Added new configuration ShderOptions.FPTLMaxLightCount in ShaderConfig.cs for maximum light count per fine pruned tile.

### Changed
- MaterialReimporter.ReimportAllMaterials and MaterialReimporter.ReimportAllHDShaderGraphs now batch the asset database changes to improve performance.
Expand All @@ -58,6 +60,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Fixed misleading text and improving the eye scene material samples. (case 1368665)
- Fixed missing DisallowMultipleComponent annotations in HDAdditionalReflectionData and HDAdditionalLightData (case 1365879).
- Fixed ambient occlusion strenght incorrectly using GTAOMultiBounce
- Maximum light count per fine prunned tile (opaque deferred) is now 63 instead of 23.

## [13.0.0] - 2021-09-01

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ Real time raytracing effect are currently in Preview and behavior could change i

<a name="Camera"></a>

### Light count limit

HDRP has a maximum limit on the number of lights a single pixel can get influence from. This setting can be configured through the ShaderConfig.cs file as the FPTLMaxLightCount. By default, HDRP's maximum light count per pixel is 63.
The only two possible values HDRP supports are Low (31 lights) and High (63 lights). For more information on how to configure this setting, please see [HDRP Config package](HDRP-Config-Package.md).

## Camera

### Post-processing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ The algorithm to calculate the contribution of ambient occlusion and specular oc

The previous `g_vLightListGlobal` uniform have been rename to explicit `g_vLightListTile` and `g_vLightListCluster` light list name. This work required to fix a wrong behavior on console.

Added a new setting in ShaderConfig.cs, FPTLMaxLightCount. This setting can now set the maximum number of lights per tile on the GPU. A new Shader config project must be generated to upgrade. See the [HDRP-Config-Package](HDRP-Config-Package.md) guide for information on how to upgrade.

## Density Volumes

Density Volumes are now known as **Local Volumetric Fog**.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ From HDRP 12.0, HDRP includes a new [Light Anchor](light-anchor.md) component. Y
![](Images/LightAnchor0.png)


### Maximum light count for gpu

The new default maximum light count per pixel for Fine Prunning Tile List is now 63.
Added a new setting in ShaderConfig.cs, FPTLMaxLightCount. This setting can now set the maximum number of lights per tile on the GPU. A new Shader config project must be generated to upgrade. See the [HDRP-Config-Package](HDRP-Config-Package.md) guide for information on how to upgrade.

## New upsampling methods

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1534,7 +1534,7 @@ void RegisterLightingDebug()
data.fullScreenContactShadowLightIndex = value;
},
min = () => - 1, // -1 will display all contact shadow
max = () => LightDefinitions.s_LightListMaxPrunedEntries - 1
max = () => ShaderConfig.FPTLMaxLightCount - 1
},
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ Shader "Hidden/HDRP/DebugViewTiles"

#ifdef DISABLE_TILE_MODE
// Tile debug mode is not supported in MSAA (only cluster)
int maxLights = 32;
int maxLights = (LIGHT_CLUSTER_PACKING_COUNT_MASK + 1);
const int textSize = 23;
const int text[textSize] = {'N', 'o', 't', ' ', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', ' ', 'w', 'i', 't', 'h', ' ', 'M', 'S', 'A', 'A'};
if (input.positionCS.y < DEBUG_FONT_TEXT_HEIGHT)
Expand All @@ -209,7 +209,7 @@ Shader "Hidden/HDRP/DebugViewTiles"
// Tile overlap counter
if (n >= 0)
{
const uint maxLightsPerTile = 31;
const uint maxLightsPerTile = SHADEROPTIONS_FPTLMAX_LIGHT_COUNT;
const float opacity = 0.3f;
result = OverlayHeatMap(int2(posInput.positionSS.xy), GetTileSize(), n, maxLightsPerTile, opacity);
}
Expand All @@ -224,8 +224,8 @@ Shader "Hidden/HDRP/DebugViewTiles"
}

// Print light lists for selected tile at the bottom of the screen
int maxLights = 32;
if (tileCoord.y < LIGHTCATEGORY_COUNT && tileCoord.x < maxLights + 3)
int maxAreaWidth = SHADEROPTIONS_FPTLMAX_LIGHT_COUNT + 4;
if (tileCoord.y < LIGHTCATEGORY_COUNT && tileCoord.x < maxAreaWidth)
{
float depthMouse = GetTileDepth(_MousePixelCoord.xy);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ private void GetContactShadowMask(HDAdditionalLightData hdAdditionalLightData, B
// If contact shadows are not enabled or we already reached the manimal number of contact shadows
// or this is not rasterization
if ((!hdAdditionalLightData.useContactShadow.Value(contactShadowEnabled))
|| m_ContactShadowIndex >= LightDefinitions.s_LightListMaxPrunedEntries)
|| m_ContactShadowIndex >= LightDefinitions.s_ContactShadowMaskMask)
return;

// Evaluate the contact shadow index of this light
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,16 @@ class LightDefinitions

// light list limits
public static int s_LightListMaxCoarseEntries = 64;
public static int s_LightListMaxPrunedEntries = 24;
public static int s_LightClusterMaxCoarseEntries = 128;

// We have room for ShaderConfig.FPTLMaxLightCount lights, plus 1 implicit value for length.
// We allocate only 16 bits per light index & length, thus we divide by 2, and store in a word buffer.
public static int s_LightDwordPerFptlTile = ((ShaderConfig.FPTLMaxLightCount + 1)) / 2;
public static int s_LightClusterPackingCountBits = (int)Mathf.Ceil(Mathf.Log(Mathf.NextPowerOfTwo(ShaderConfig.FPTLMaxLightCount), 2));
public static int s_LightClusterPackingCountMask = (1 << s_LightClusterPackingCountBits) - 1;
public static int s_LightClusterPackingOffsetBits = 32 - s_LightClusterPackingCountBits;
public static int s_LightClusterPackingOffsetMask = (1 << s_LightClusterPackingOffsetBits) - 1;

// Following define the maximum number of bits use in each feature category.
public static uint s_LightFeatureMaskFlags = 0xFFF000;
public static uint s_LightFeatureMaskFlagsOpaque = 0xFFF000 & ~((uint)LightFeatureFlags.SSRefraction); // Opaque don't support screen space refraction
Expand All @@ -121,6 +128,13 @@ class LightDefinitions
public static uint s_ScreenSpaceColorShadowFlag = 0x100;
public static uint s_InvalidScreenSpaceShadow = 0xff;
public static uint s_ScreenSpaceShadowIndexMask = 0xff;

//Contact shadow bit definitions
public static int s_ContactShadowFadeBits = 8;
public static int s_ContactShadowMaskBits = 32 - s_ContactShadowFadeBits;
public static int s_ContactShadowFadeMask = (1 << s_ContactShadowFadeBits) - 1;
public static int s_ContactShadowMaskMask = (1 << s_ContactShadowMaskBits) - 1;

}

[GenerateHLSL]
Expand Down Expand Up @@ -2131,14 +2145,15 @@ bool WillRenderContactShadow()

// The first rendered 24 lights that have contact shadow enabled have a mask used to select the bit that contains
// the contact shadow shadowed information (occluded or not). Otherwise -1 is written
// 8 bits are reserved for the fading.
void GetContactShadowMask(HDAdditionalLightData hdAdditionalLightData, BoolScalableSetting contactShadowEnabled, HDCamera hdCamera, bool isRasterization, ref int contactShadowMask, ref float rayTracingShadowFlag)
{
contactShadowMask = 0;
rayTracingShadowFlag = 0.0f;
// If contact shadows are not enabled or we already reached the manimal number of contact shadows
// or this is not rasterization
if ((!hdAdditionalLightData.useContactShadow.Value(contactShadowEnabled))
|| m_ContactShadowIndex >= LightDefinitions.s_LightListMaxPrunedEntries
|| m_ContactShadowIndex >= LightDefinitions.s_ContactShadowMaskMask
|| !isRasterization)
return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@
#define TILE_INDEX_SHIFT_EYE (30)
#define NUM_FEATURE_VARIANTS (29)
#define LIGHT_LIST_MAX_COARSE_ENTRIES (64)
#define LIGHT_LIST_MAX_PRUNED_ENTRIES (24)
#define LIGHT_CLUSTER_MAX_COARSE_ENTRIES (128)
#define LIGHT_DWORD_PER_FPTL_TILE (32)
#define LIGHT_CLUSTER_PACKING_COUNT_BITS (6)
#define LIGHT_CLUSTER_PACKING_COUNT_MASK (63)
#define LIGHT_CLUSTER_PACKING_OFFSET_BITS (26)
#define LIGHT_CLUSTER_PACKING_OFFSET_MASK (67108863)
#define LIGHT_FEATURE_MASK_FLAGS (16773120)
#define LIGHT_FEATURE_MASK_FLAGS_OPAQUE (16642048)
#define LIGHT_FEATURE_MASK_FLAGS_TRANSPARENT (16510976)
Expand All @@ -58,6 +62,10 @@
#define SCREEN_SPACE_COLOR_SHADOW_FLAG (256)
#define INVALID_SCREEN_SPACE_SHADOW (255)
#define SCREEN_SPACE_SHADOW_INDEX_MASK (255)
#define CONTACT_SHADOW_FADE_BITS (8)
#define CONTACT_SHADOW_MASK_BITS (24)
#define CONTACT_SHADOW_FADE_MASK (255)
#define CONTACT_SHADOW_MASK_MASK (16777215)

//
// UnityEngine.Rendering.HighDefinition.ClusterDebugMode: static fields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/LightLoop.cs.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/LightLoop/CookieSampling.hlsl"

#define DWORD_PER_TILE 16 // See dwordsPerTile in LightLoop.cs, we have roomm for 31 lights and a number of light value all store on 16 bit (ushort)

// Some file may not required HD shadow context at all. In this case provide an empty one
// Note: if a double defintion error occur it is likely have include HDShadow.hlsl (and so HDShadowContext.hlsl) after lightloopdef.hlsl
#ifndef HAVE_HD_SHADOW_CONTEXT
Expand Down Expand Up @@ -192,7 +190,7 @@ void GetCountAndStartTile(PositionInputs posInput, uint lightCategory, out uint
#endif

// The first entry inside a tile is the number of light for lightCategory (thus the +0)
lightCount = g_vLightListTile[DWORD_PER_TILE * tileOffset + 0] & 0xffff;
lightCount = g_vLightListTile[LIGHT_DWORD_PER_FPTL_TILE * tileOffset + 0] & 0xffff;
start = tileOffset;
}

Expand All @@ -212,7 +210,7 @@ uint FetchIndex(uint tileOffset, uint lightOffset)
{
const uint lightOffsetPlusOne = lightOffset + 1; // Add +1 as first slot is reserved to store number of light
// Light index are store on 16bit
return (g_vLightListTile[DWORD_PER_TILE * tileOffset + (lightOffsetPlusOne >> 1)] >> ((lightOffsetPlusOne & 1) * DWORD_PER_TILE)) & 0xffff;
return (g_vLightListTile[LIGHT_DWORD_PER_FPTL_TILE * tileOffset + (lightOffsetPlusOne >> 1)] >> ((lightOffsetPlusOne & 1) * 16)) & 0xffff;
}

#elif defined(USE_CLUSTERED_LIGHTLIST)
Expand All @@ -236,15 +234,20 @@ uint GetLightClusterIndex(uint2 tileIndex, float linearDepth)
return SnapToClusterIdxFlex(linearDepth, logBase, g_isLogBaseBufferEnabled != 0);
}

void UnpackClusterLayeredOffset(uint packedValue, out uint offset, out uint count)
{
offset = packedValue & LIGHT_CLUSTER_PACKING_OFFSET_MASK;
count = packedValue >> LIGHT_CLUSTER_PACKING_OFFSET_BITS;
}

void GetCountAndStartCluster(uint2 tileIndex, uint clusterIndex, uint lightCategory, out uint start, out uint lightCount)
{
int nrClusters = (1 << g_iLog2NumClusters);

const int idx = GenerateLayeredOffsetBufferIndex(lightCategory, tileIndex, clusterIndex, _NumTileClusteredX, _NumTileClusteredY, nrClusters, unity_StereoEyeIndex);

uint dataPair = g_vLayeredOffsetsBuffer[idx];
start = dataPair & 0x7ffffff;
lightCount = (dataPair >> 27) & 31;
UnpackClusterLayeredOffset(dataPair, start, lightCount);
}

void GetCountAndStartCluster(PositionInputs posInput, uint lightCategory, out uint start, out uint lightCount)
Expand Down Expand Up @@ -346,16 +349,17 @@ EnvLightData FetchEnvLight(uint index)
return _EnvLightDatas[index];
}

// In the first 8 bits of the target we store the max fade of the contact shadows as a byte
// In the first bits of the target we store the max fade of the contact shadows as a byte.
//By default its 8 bits for the fade and 24 for the mask, please check the LightLoop.cs definitions.
void UnpackContactShadowData(uint contactShadowData, out float fade, out uint mask)
{
fade = float(contactShadowData >> 24) / 255.0;
mask = contactShadowData & 0xFFFFFF; // store only the first 24 bits which represent
fade = float(contactShadowData >> CONTACT_SHADOW_MASK_BITS) / ((float)CONTACT_SHADOW_FADE_MASK);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yeah contact shadow are gonna be awkward :D I wonder if we should change a bit how we do it later on.

Now if we have only one light that has contact shadow, but that happens to have an index bigger than 24 in the list it will not show up.

Even worse if that light changes depending on culling from say 20 and 25 as index, it will "flicker" contact shadows :/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remind exactly but aren't we sorting light by shadow casting? i.e the first set of light in the list currently cast shadow? increasing the likelihood that we have shadow on the first 24 light index. Guess something to test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there is no sorting by shadow casting as far as I can tell. We sort by light type on the entire frame, then we sort per light index per tile.
The way it works is that we allocate all contact shadows from scratch every frame on all lights on the view. Not on a per tile basis.

The very first 24 lights that Have shadows enabled get it, and the allocation is on the CPU. Note that we can have 1 light with contact shadows followed by 32 that dont, and then 24 more that have contact shadows. In this case it will be perfectly fine.

The only way shadow allocation can vary is if a new light enters the view (passes culling).

For example, we have in the frame 512 lights in view. The first one and last 23 ones have contact shadows enabled.

  • In this situation we will never get flickering

Now lets assume we have 1 more light with contact shadow, now we have a total of 25.
If all lights are on view, it wont flicker either because the sorting in the CPU remains deterministic.

Hope this makes sense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah stuff changed and forgot, used to be that each bit of the 24 bit mapped to the index of the light in the tile (and there 24 was convenient for that :P )

mask = contactShadowData & CONTACT_SHADOW_MASK_MASK; // store only the first 24 bits which represent
}

uint PackContactShadowData(float fade, uint mask)
{
uint fadeAsByte = (uint(saturate(fade) * 255) << 24);
uint fadeAsByte = (uint(saturate(fade) * CONTACT_SHADOW_FADE_MASK) << CONTACT_SHADOW_MASK_BITS);

return fadeAsByte | mask;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ StructuredBuffer<SFiniteLightBound> g_data : register( t3 );
StructuredBuffer<uint> g_vBigTileLightList : register( t4 ); // don't support Buffer yet in unity
#endif


#ifdef PLATFORM_LANE_COUNT // We can infer the size of a wave. This is currently not possible on non-consoles, so we have to fallback to a sensible default in those cases.
#define NR_THREADS PLATFORM_LANE_COUNT
#else
Expand All @@ -72,6 +71,11 @@ groupshared uint lightOffs;
groupshared int categoryListCountScratch[NR_THREADS * LIGHTCATEGORY_COUNT];
groupshared int shiftIndexScratch[NR_THREADS * LIGHTCATEGORY_COUNT];

uint PackClusterLayeredOffset(uint offset, uint count)
{
return (offset & LIGHT_CLUSTER_PACKING_OFFSET_MASK) | (min(count, LIGHT_CLUSTER_PACKING_COUNT_MASK) << LIGHT_CLUSTER_PACKING_OFFSET_BITS);
}

void ZeroCategoryListCountAndShiftIndex(uint threadIdx)
{
for (int i = 0; i < LIGHTCATEGORY_COUNT; ++i)
Expand Down Expand Up @@ -445,12 +449,12 @@ void LIGHTLISTGEN(uint threadID : SV_GroupIndex, uint3 u3GroupID : SV_GroupID)
offs = GenerateLayeredOffsetBufferIndex(0, tileIDX, i, nrTilesX, nrTilesY, nrClusters, eyeIndex);
for(int category=0; category<LIGHTCATEGORY_COUNT; category++)
{
int numLights = min(ReadCategoryListCount(t, category),31); // only allow 5 bits
int numLights = ReadCategoryListCount(t, category);
if(i<nrClusters)
{
g_LayeredOffset[offs] = (start+localOffs) | (((uint) numLights)<<27);
g_LayeredOffset[offs] = PackClusterLayeredOffset((start+localOffs), (uint)numLights);
offs += (nrClusters*nrTilesX*nrTilesY);
localOffs += ReadCategoryListCount(t, category); // use unclamped count for localOffs
localOffs += numLights;// use unclamped count for localOffs
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ void TileLightListGen(uint3 dispatchThreadId : SV_DispatchThreadID, uint threadI
for(int category=0; category<CATEGORY_LIST_SIZE; category++)
{
int nrLightsFinal = ldsCategoryListCount[category];
int nrLightsFinalClamped = nrLightsFinal<LIGHT_LIST_MAX_PRUNED_ENTRIES ? nrLightsFinal : LIGHT_LIST_MAX_PRUNED_ENTRIES;
int nrLightsFinalClamped = nrLightsFinal<SHADEROPTIONS_FPTLMAX_LIGHT_COUNT ? nrLightsFinal : SHADEROPTIONS_FPTLMAX_LIGHT_COUNT;

const int nrDWords = ((nrLightsFinalClamped+1)+1)>>1;
for(int l=(int) t; l<(int) nrDWords; l += NR_THREADS)
Expand All @@ -337,7 +337,7 @@ void TileLightListGen(uint3 dispatchThreadId : SV_DispatchThreadID, uint threadI
uint uLow = l==0 ? nrLightsFinalClamped : prunedList[max(0,2 * l - 1 + localOffs)] - shiftIndex[category];
uint uHigh = prunedList[2 * l + 0 + localOffs] - shiftIndex[category];

g_vLightList[16*offs + l] = (uLow&0xffff) | (uHigh<<16);
g_vLightList[LIGHT_DWORD_PER_FPTL_TILE*offs + l] = (uLow&0xffff) | (uHigh<<16);
}

localOffs += nrLightsFinal;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,15 +589,13 @@ unsafe void PrepareBuildGPULightListPassData(
var nrTilesX = (m_MaxCameraWidth + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl;
var nrTilesY = (m_MaxCameraHeight + LightDefinitions.s_TileSizeFptl - 1) / LightDefinitions.s_TileSizeFptl;
var nrTiles = nrTilesX * nrTilesY * m_MaxViewCount;
const int capacityUShortsPerTile = 32;
const int dwordsPerTile = (capacityUShortsPerTile + 1) >> 1; // room for 31 lights and a nrLights value.

if (tileAndClusterData.hasTileBuffers)
{
// note that nrTiles include the viewCount in allocation below
// Tile buffers
passData.output.lightList = builder.WriteComputeBuffer(
renderGraph.CreateComputeBuffer(new ComputeBufferDesc((int)LightCategory.Count * dwordsPerTile * nrTiles, sizeof(uint)) { name = "LightList" }));
renderGraph.CreateComputeBuffer(new ComputeBufferDesc((int)LightCategory.Count * LightDefinitions.s_LightDwordPerFptlTile * nrTiles, sizeof(uint)) { name = "LightList" }));
passData.output.tileList = builder.WriteComputeBuffer(
renderGraph.CreateComputeBuffer(new ComputeBufferDesc(LightDefinitions.s_NumFeatureVariants * nrTiles, sizeof(uint)) { name = "TileList" }));
passData.output.tileFeatureFlags = builder.WriteComputeBuffer(
Expand Down