internal SinglePassTextureFilter(DrawTargetTexture2D source, DrawTargetTexture2D target) { if (source == null || target == null) { throw new ArgumentNullException(); } if (source.SurfaceFormat != target.SurfaceFormat) { throw new ArgumentException("source.SurfaceFormat != target.SurfaceFormat"); } #if XBOX360 if (source == target && source.Width * source.Height * DrawTarget.FormatSize(source.SurfaceFormat) > 1000 * 1000 * 10) //approx 10mb { throw new ArgumentException("source == target is invalid for larget render targets on Xbox360"); } #else if (source == target) { throw new ArgumentException("source == target is invalid on windows"); } #endif if (source.Width > target.Width || source.Height > target.Height) { throw new ArgumentException("source is larger than target"); } this.source = source; this.targetClone = target.Clone(false, false, false); this.targetClone.ClearBuffer.Enabled = false; this.element = new ShaderElement(null, source.Width, source.Height, new Vector2(1, 1), true); this.targetClone.Add(element); if (target.Width != source.Width || target.Height != source.Height) { this.targetClone.AddModifier(new Xen.Graphics.Modifier.ScissorModifier(0, 0, Math.Min(1, (source.Width) / target.Width), Math.Min(1, (source.Height) / target.Height))); this.element.TextureCrop = new Rectangle(0, 0, Math.Min(this.source.Width, this.targetClone.Width), Math.Min(this.source.Height, this.targetClone.Height)); } }
/// <summary> /// Construct the texture downsampler /// </summary> /// <param name="source">Source texture to read</param> /// <param name="target">Target texture to write to</param> /// <param name="intermediate">Intermediate texture (if null, will be created as required)</param> /// <param name="intermediate2">Second intermediate texture (if null, will be created as required)</param> /// <param name="targetWidth">target width to downsample to</param> /// <param name="targetHeight">target height to downsample to</param> public TextureDownsample(DrawTargetTexture2D source, DrawTargetTexture2D target, ref DrawTargetTexture2D intermediate, ref DrawTargetTexture2D intermediate2, int targetWidth, int targetHeight) { if (source == null || target == null) { throw new ArgumentNullException(); } if (targetWidth <= 0 || targetHeight <= 0) { throw new ArgumentException("Invalid target size"); } if (DrawTarget.FormatChannels(source.SurfaceFormat) != DrawTarget.FormatChannels(target.SurfaceFormat)) { throw new ArgumentException("source.SurfaceFormat has a different number of channels than target.SurfaceFormat"); } if (targetWidth > target.Width || targetHeight > target.Height) { throw new ArgumentException("Size is larger than target"); } if (targetWidth > source.Width || targetHeight > source.Height) { throw new ArgumentException("Size is larger than source"); } if (intermediate != null) { if (target.SurfaceFormat != intermediate.SurfaceFormat) { throw new ArgumentException("target.SurfaceFormat != intermediate.SurfaceFormat"); } if (intermediate == intermediate2) { throw new ArgumentException("intermediate == intermediate2"); } } if (intermediate2 != null) { if (target.SurfaceFormat != intermediate2.SurfaceFormat) { throw new ArgumentException("target.SurfaceFormat != intermediate2.SurfaceFormat"); } } int w = source.Width; int h = source.Height; int targetMultipleWidth = targetWidth; int targetMultipleHeight = targetHeight; while (targetMultipleWidth * 2 <= w) { targetMultipleWidth *= 2; } while (targetMultipleHeight * 2 <= h) { targetMultipleHeight *= 2; } DrawTargetTexture2D current = null; Rectangle sRegion = new Rectangle(0, 0, 0, 0); //first pass may require that the source is sized down to a multiple of the target size if ((double)targetWidth / (double)w <= 0.5 && (double)targetHeight / (double)h <= 0.5 && (targetMultipleWidth != w || targetMultipleHeight != h)) { DrawTargetTexture2D go = this.PickRT(ref intermediate, ref intermediate2, source, targetMultipleWidth, targetMultipleHeight, target.SurfaceFormat); Vector2 size = new Vector2((float)targetMultipleWidth, (float)targetMultipleHeight); TexturedElement te = new TexturedElement(source, size, false); te.TextureCrop = new Rectangle(0, 0, w, h); go.Add(te); passes.Add(go); current = go; w = targetMultipleWidth; h = targetMultipleHeight; } //downsample on the larger axis, either 2x, 4x or 8x downsampling, until reached the target size while (target.Equals(current) == false) { DrawTargetTexture2D localSource = current ?? source; double difW = (double)targetWidth / (double)w; double difH = (double)targetHeight / (double)h; sRegion.Width = w; sRegion.Height = h; sRegion.Y = localSource.Height - h; //both width/height difference are less than 50% smaller, so a linear interpolation will do fine if (difW > 0.5 && difH > 0.5) { //write directly to the target DrawTargetTexture2D go = target.Clone(false, false, false); Vector2 te_size = new Vector2((float)targetWidth, (float)targetHeight); TexturedElement te = new TexturedElement(localSource, te_size, false); go.AddModifier(new ScissorModifier(0, go.Height - targetHeight, targetWidth, go.Height, go)); te.TextureCrop = sRegion; go.Add(te); passes.Add(go); current = go; continue; } bool horizontal = difW < difH; double dif = Math.Min(difW, difH); int size = horizontal ? w : h; Vector2 dir = new Vector2(0, 0); if (horizontal) { dir.X = 1.0f / localSource.Width; } else { dir.Y = 1.0f / localSource.Height; } if (dif > 0.25) // cutoff for using 2 samples { DrawTargetTexture2D go; int new_width = w; int new_height = h; if (horizontal) { new_width /= 2; } else { new_height /= 2; } if (new_width == targetWidth && new_height == targetHeight) { go = target.Clone(false, false, false); } else { go = PickRT(ref intermediate, ref intermediate2, localSource, new_width, new_height, target.SurfaceFormat); } Vector2 se_size = new Vector2((float)new_width, (float)new_height); ShaderElement se = new ShaderElement(new Downsample2(), se_size, false); go.AddModifier(new ScissorModifier(0, go.Height - new_height, new_width, go.Height, go)); se.TextureCrop = sRegion; go.Add(new Drawer(dir, se, localSource)); passes.Add(go); w = new_width; h = new_height; current = go; continue; } if (dif > 0.125) // cutoff for using 4 samples { DrawTargetTexture2D go; int new_width = w; int new_height = h; if (horizontal) { new_width /= 4; } else { new_height /= 4; } if (new_width == targetWidth && new_height == targetHeight) { go = target.Clone(false, false, false); } else { go = PickRT(ref intermediate, ref intermediate2, localSource, new_width, new_height, target.SurfaceFormat); } Vector2 se_size = new Vector2((float)new_width, (float)new_height); ShaderElement se = new ShaderElement(new Downsample4(), se_size, false); go.AddModifier(new ScissorModifier(0, go.Height - new_height, new_width, go.Height, go)); se.TextureCrop = sRegion; go.Add(new Drawer(dir, se, localSource)); passes.Add(go); w = new_width; h = new_height; current = go; continue; } // cutoff for using 8 samples { DrawTargetTexture2D go; int new_width = w; int new_height = h; if (horizontal) { new_width /= 8; } else { new_height /= 8; } if (new_width == targetWidth && new_height == targetHeight) { go = target.Clone(false, false, false); } else { go = PickRT(ref intermediate, ref intermediate2, localSource, new_width, new_height, target.SurfaceFormat); } Vector2 se_size = new Vector2((float)new_width, (float)new_height); ShaderElement se = new ShaderElement(new Downsample8(), se_size, false); go.AddModifier(new ScissorModifier(0, go.Height - new_height, new_width, go.Height, go)); se.TextureCrop = sRegion; go.Add(new Drawer(dir, se, localSource)); passes.Add(go); w = new_width; h = new_height; current = go; continue; } } }