// todo for tomorrow // stop allocating meshes, pool materials // handle not drawing screen aligned clipping bounds // handle multiple clip shapes in a path // maybe handle textures // handle lots of clip shapes // handle masking channels // profile public void ConstructClipData() { maskPackerR.Clear(); maskPackerG.Clear(); maskPackerB.Clear(); maskPackerA.Clear(); // todo -- profile sorting by size // might not need the -1 if screen level one is never sent // also don't need to render clipper for a view since it is automatically rectangular // basically any pure screen aligned rectangle doesn't need to be rendered out for (int i = 0; i < clippers.size; i++) { ClipData clipData = clippers.array[i]; if (clipData.clipPath == null) { float xy = VertigoUtil.PackSizeVector(new Vector2(clipData.aabb.x, clipData.aabb.y)); float zw = VertigoUtil.PackSizeVector(new Vector2(clipData.aabb.z, clipData.aabb.w)); clipData.packedBoundsAndChannel.x = xy; clipData.packedBoundsAndChannel.y = zw; continue; } clipData.zIndex = i + 1; // will have to change depending on how we decide to handle different channels int width = (int)(clipData.aabb.z - clipData.aabb.x); int height = (int)(clipData.aabb.w - clipData.aabb.y); SimpleRectPacker.PackedRect region; clipData.textureChannel = -1; if (maskPackerR.TryPackRect(width, height, out region)) { clipData.textureChannel = 0; } // todo -- other channels don't work right now, need to figure out if we use additional draw calls or super double dip region packing // else if (maskPackerG.TryPackRect(width, height, out region)) { // clipData.textureChannel = 1; // } // else if (maskPackerB.TryPackRect(width, height, out region)) { // clipData.textureChannel = 2; // } // else if (maskPackerA.TryPackRect(width, height, out region)) { // clipData.textureChannel = 3; // } else { Debug.Log($"Can't fit {width}, {height} into clip texture"); } // note this is in 2 point form, not height & width if (clipData.textureChannel != -1) { clipData.clipTexture = clipTexture; clipData.textureRegion = region; clipData.clipUVs = new Vector4( region.xMin / (float)clipTexture.width, region.yMin / (float)clipTexture.height, region.xMax / (float)clipTexture.width, region.yMax / (float)clipTexture.height ); float xy = VertigoUtil.PackSizeVector(new Vector2(clipData.aabb.x, clipData.aabb.y)); float zw = VertigoUtil.PackSizeVector(new Vector2(clipData.aabb.z, clipData.aabb.w)); clipData.packedBoundsAndChannel.x = xy; clipData.packedBoundsAndChannel.y = zw; clipData.packedBoundsAndChannel.z = clipData.textureChannel; } } }
// depth buffer means our regions are locked per channel // 2 options: 1. each channel is its own set of draw calls, this is easy but maybe not as fast. do this as a first pass // 2. try to re-use regions for different channels, almost certainly leads to less throughput but faster since we don't need extra draw calls // probably means we have sub-sorting regions, ie large packers would have sub-packers // would definitely want to sort by size in that case and first try to pack larger regions into themselves // would likely update rect packer to be channel aware, when trying to place next item instead of moving over try colliding a different channel instead public void Clip(Camera camera, CommandBuffer commandBuffer) { // breaks on refresh if we don't do this :( this.clearMaterial.SetColor(s_Color, Color.white); this.clearCountMaterial.SetColor(s_Color, new Color(0, 0, 0, 0)); requireRegionCounting = false; for (int i = 0; i < batchesToRender.size; i++) { batchesToRender[i].pooledMesh.Release(); StructList <Matrix4x4> .Release(ref batchesToRender.array[i].transforms); StructList <Vector4> .Release(ref batchesToRender.array[i].objectData); StructList <Vector4> .Release(ref batchesToRender.array[i].colorData); } batchesToRender.Clear(); Gather(); Vector3 cameraOrigin = camera.transform.position; cameraOrigin.x -= 0.5f * Screen.width; cameraOrigin.y += (0.5f * Screen.height); cameraOrigin.z += 2; Matrix4x4 origin = Matrix4x4.TRS(cameraOrigin, Quaternion.identity, Vector3.one); LightList <ClipData> texturedClippers = LightList <ClipData> .Get(); regionMesh?.Release(); regionMesh = GetRegionMesh(out requireRegionCounting); clipTexture = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.Default); // todo -- use lower resolution #if DEBUG commandBuffer.BeginSample("UIFora Clip Draw"); #endif commandBuffer.SetRenderTarget(clipTexture); // probably don't need this actually, can bake it into clear. keep for debugging commandBuffer.ClearRenderTarget(true, true, Color.black); commandBuffer.DrawMesh(regionMesh.mesh, origin, clearMaterial, 0, 0); // todo -- handle multiple shapes from one path ClipBatch batch = new ClipBatch(); batch.transforms = StructList <Matrix4x4> .Get(); batch.colorData = StructList <Vector4> .Get(); batch.objectData = StructList <Vector4> .Get(); for (int i = 0; i < clippers.size; i++) { ClipData clipData = clippers[i]; Path2D clipPath = clipData.clipPath; if (clipPath == null) { // todo if transform is not identity we need to generate a rotated or skewed rect for the clip shape continue; } clipPath.UpdateGeometry(); // should early out if no update required if (AnyShapeUsesTextures(clipPath)) { // todo -- handle textures // todo -- handle text continue; } batch = DrawShapesInPath(batch, clipPath, clipData, clipData); for (int j = 0; j < clipData.dependents.size; j++) { batch = DrawShapesInPath(batch, clipPath, clipData, clipData.dependents[j]); } } FinalizeBatch(batch, false); for (int i = 0; i < batchesToRender.size; i++) { ref ClipBatch clipBatch = ref batchesToRender.array[i]; ClipPropertyBlock propertyBlock = clipMaterialPool.GetPropertyBlock(clipBatch.transforms.size); propertyBlock.SetData(clipBatch); commandBuffer.DrawMesh(clipBatch.pooledMesh.mesh, origin, clipDrawMaterial, 0, 0, propertyBlock.matBlock); }
internal void AddClipper(ClipData clipData) { clippers.Add(clipData); }