mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-02-08 13:40:58 -05:00
* DDGI stub with rasterized path * Debug probes and fix stuff * Simplify all of this, add system later * Iterate, start integrate * Integrate depth, allow copying depth to another format * Iterate, fix depth, send ddgi volume data to gpu, start integrating on lighting compositing, almost there * DDGIVolume gets bake button, doesnt bake automatically, correct transformation * DDGI Visibility Tests wip * DDGI Volumetric Fog https://files.facepunch.com/sampavlovic/1b2911b1/sbox-dev_UCziuQjdTQ.mp4 * Fix ddgi depth integration, iterate * Simplify this and correct octahedral coordinates seams * Iterate * Probe is 8x8, do normalization directly, cleanup * Bias probe position, visibility adjustments, make eveyrthing smoother * Unify integration in SampleProbeData, still need to clean it further * Unify integrate passes * Add view bias and use border as per papers, fixes most distortion when using visibility at the same time https://files.facepunch.com/sampavlovic/1b0411b1/sbox-dev_laDCpwFxk5.png * Cleanup and fixes for border, cleanup sampling code too * Proper compositing for DDGI for maps that have baked lighting already, could still have more thought to avoid uncessesary branching * DDGI System, allow for multiple volumes in scene, cleanup https://files.facepunch.com/sampavlovic/1b0711b1/sbox-dev_xcJUeit2s4.png https://files.facepunch.com/sampavlovic/1b0711b1/sbox-dev_c59O7Bea6p.png * Rebase fixes, the way to do this sucks * Caching tests, Texture.GetPixels3D seems a bit messed * ddgi.texturecache texturegenerator, much simpler, caches texture nicely and doesnt lead residues on ram after uploaded * Add LPVDebugGrid on managed, make DDGIVolume use it, dont save cache texture on disable, works way faster than gizmos * Update volume only if it's enabled * DDGI on AmbientLight.hlsl * Simplify and fixes * ExtendToSceneBounds, rebake envmaps when bake finished, normalbias * RENDER_DEFAULT_STATE_RASTERIZER probably shouldnt remove override of rasterizer state * Cleanup * [Pick] Fix TextureCompiler not compiling 3D textures properly, make Texture.GetBitmap work with 3D textures * Add NoPrepassCulling, avoids "rays" from probes that are inside geometry from leaking light https://files.facepunch.com/sampavlovic/1b1611b1/sbox-dev_bQfsZlWwop.png * Final adjustments and cleanupj, name this Indirect Light Volume * Indirect Light Volume prefab * Remove log infos, adjust sliders * Whoops * format * Rebase fix * Re-apply the currently tracked cull mode so the override state binds immediately, makes sure that SetLayerNoCull works as intended * Enqueue probes that affect geometry first, this gives quicker iteration results https://files.facepunch.com/sampavlovic/1b1911b1/Screen_Recording_2026-01-19_at_16.16.36.mov * float16 guards for ddgi_integrate, avoid NaNs * Texture.Save supports volume textures * DDGIVolume saves using the super nice Texture.Save api instead of texutre generator * Do the same color punchy look thing we do for envmap probes in ddgi probes https://files.facepunch.com/sampavlovic/1b1911b1/ezgif-13a1dd53fc7688fe.gif * Don't save ddgi async, just do it directly * DDGI editor icon https://files.facepunch.com/sampavlovic/1b2011b1/F1I0KaZt5j.png * Shaders * Format * Add EditorSystem.Camera * Move texture creation to DDGIProbeUpdaterCubemapper, build probes in order from the camera, use async instead of an update * Dont multiply DDGI diffuse by AO, that's done on the shadingmodel, this gives accurate multibounce as well * Update probes sequentially again, makes sure results are consistent, if we do near camera again, should do it with same logic to keep probes that affect geometry first too * Move NoPrepassCulling to RenderToCubeTexture, can't figure out why behaviour is different between these but solves light leak issue * Fix overriding array not working for depth textures, make this clearer * Overriden rasterizer state actually overrides when doing SetCullMode * Rework border logic, should be seamless now * Experiment with shadow sharpness being soft, with borders being fine the variance shadow map stuff from DDGI paper looks correct https://files.facepunch.com/sampavlovic/1b2211b1/sbox-dev_UNTiRmJ7Fw.png * Make Depth Sharpness a Property instead * Delete copy_border shader, do everything within the same shader with groupshared memory, actually sample borders correctly, this reduces octahedral border aliasing to a minimum https://files.facepunch.com/sampavlovic/1b2311b1/sbox-dev_FqPPpRZ6MD.png https://files.facepunch.com/sampavlovic/1b2311b1/Source2Viewer_TiqGxdYWwX.png * Distance calculations use unbiased probe-space position so when we move ddgi transform it doesnt fuck visibility up * Make DDGI Debug Grid use gizmo pooling so it follows gizmo visibility rules (hidden when gizmos disabled, not in cubemaps) * DDGI Relocation proto, Moves probes out of geometry to unfuck artifacts https://files.facepunch.com/sampavlovic/1b2311b1/ezgif-1afaed4e1c2ac9a5.gif https://files.facepunch.com/sampavlovic/1b2311b1/ezgif-11a3e64fd2952590.gif * DDGI less contrasty * Parallelize relocation * More resilient tracing tests & give option to deactivate if probe is fully inside of geometry instead of relocating those fully occluded * Simplify LPVDebugGrid, remove all the fucking mess from it * Simplify DDGIVolume, hide all the bullshit * VTexWriter allows to save as uncompressed, make Relocation texture uncompressed since we want alpha to be flags * Add Graphics.FlushGPU Forces the GPU to flush all pending commands and wait for completion. Useful when you need to ensure GPU work is finished before proceeding. Can be called outside of a render block. * DDGIVolume uses Graphics.FlushGPU instead of dumb Task.Await, density goes up to 10, Debug Grid updates if we change probes * Format * Doubt we are going to use flags, just store alpha as active state directly, makes it more resilient to eventual compression * Sprite was calculating offset incorrectly and causing ambient lighting to be fucked, fog was fucked too https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_i3h4RcWncI.png * DDGI should really be under Light not Misc * Keep LPVDebugGrid render bounds infinite, SceneCusotmObject shouldnt change it from there * Move RelocationTexture to same Generated behaviour so we dont override the property it while we are rendering, make probes default visible * Remove shitty hack from scenecamera and make DDGIVolumeUpdater take scene renderattributes, makes DDGI bounces retrofeed from itself Should fix terrain rendering being fucked in DDGI too Will see to make this more generic https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_uzXYN0Qzw6.png * Make Energy Loss an attribute for DDGI, I prefer a more contrasty look but there are scenes where you'd want more accurate energy conservation, first picture is an example where energy loss contrast makes it look worse https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_kdTH1qhEwR.png https://files.facepunch.com/sampavlovic/1b2611b1/sbox-dev_gMoRxorzV0.png * Progress system can be toasts * Clean up progress system, always available now via Application.Editor. * Added Scene/Bake DDGI Volumes * Changed DDGIVolume to IndirectLightVolume * Refinement steps for probe relocation * Kill existing bake if disabled or ticking bake again * EnergyLoss is Contrast and increase max density, keep default contrast to the value I'm happy with * Don't call it the implementation name to the user * formet * Build shaders * Bind dummy DDGI_Volumes buffer as default on high level --------- Co-authored-by: Garry Newman <garrynewman@users.noreply.github.com>
447 lines
16 KiB
C#
447 lines
16 KiB
C#
using NativeEngine;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace Sandbox;
|
|
|
|
/// <summary>
|
|
/// Used to render to the screen using your Graphics Card, or whatever you
|
|
/// kids are using in your crazy future computers. Whatever it is I'm sure
|
|
/// it isn't fungible and everyone has free money and no-one has to ever work.
|
|
/// </summary>
|
|
public static partial class Graphics
|
|
{
|
|
public enum PrimitiveType
|
|
{
|
|
Points = NativeEngine.RenderPrimitiveType.RENDER_PRIM_POINTS,
|
|
Lines = NativeEngine.RenderPrimitiveType.RENDER_PRIM_LINES,
|
|
LinesWithAdjacency = NativeEngine.RenderPrimitiveType.RENDER_PRIM_LINES_WITH_ADJACENCY,
|
|
LineStrip = NativeEngine.RenderPrimitiveType.RENDER_PRIM_LINE_STRIP,
|
|
LineStripWithAdjacency = NativeEngine.RenderPrimitiveType.RENDER_PRIM_LINE_STRIP_WITH_ADJACENCY,
|
|
Triangles = NativeEngine.RenderPrimitiveType.RENDER_PRIM_TRIANGLES,
|
|
TrianglesWithAdjacency = NativeEngine.RenderPrimitiveType.RENDER_PRIM_TRIANGLES_WITH_ADJACENCY,
|
|
TriangleStrip = NativeEngine.RenderPrimitiveType.RENDER_PRIM_TRIANGLE_STRIP,
|
|
TriangleStripWithAdjacency = NativeEngine.RenderPrimitiveType.RENDER_PRIM_TRIANGLE_STRIP_WITH_ADJACENCY,
|
|
}
|
|
|
|
/// <summary>
|
|
/// If true then we're currently rendering and
|
|
/// you are safe to use the contents of this class
|
|
/// </summary>
|
|
public static bool IsActive => _state.active;
|
|
|
|
/// <summary>
|
|
/// The current layer type. This is useful to tell whether you're meant to be drawing opaque, transparent or shadow. You mainly
|
|
/// don't need to think about this, but when you do, it's here.
|
|
/// </summary>
|
|
public static SceneLayerType LayerType => _state.layerType;
|
|
|
|
struct RenderState
|
|
{
|
|
public bool active;
|
|
public IRenderContext renderContext;
|
|
public ISceneLayer sceneLayer;
|
|
public ISceneView sceneView;
|
|
public SceneLayerType layerType;
|
|
public RenderAttributes attributes;
|
|
internal SceneSystemPerFrameStats_t stats;
|
|
public ImageFormat colorFormat;
|
|
public MultisampleAmount msaaLevel;
|
|
public Transform cameraTransform;
|
|
internal RenderAttributes frameAttributes;
|
|
internal RenderTarget renderTarget;
|
|
internal float defaultMinZ;
|
|
internal float defaultMaxZ;
|
|
}
|
|
|
|
[ThreadStatic]
|
|
private static RenderState _state;
|
|
|
|
internal static IRenderContext Context => _state.renderContext;
|
|
internal static ISceneLayer SceneLayer => _state.sceneLayer;
|
|
internal static ISceneView SceneView => _state.sceneView;
|
|
internal static ImageFormat IdealColorFormat => _state.colorFormat;
|
|
internal static MultisampleAmount IdealMsaaLevel => _state.msaaLevel;
|
|
internal static SceneSystemPerFrameStats_t Stats => _state.stats;
|
|
|
|
/// <summary>
|
|
/// When Frame grabbing, we store the result in this pooled render target. It stays checked out
|
|
/// until the end of this render scope.
|
|
/// </summary>
|
|
[ThreadStatic]
|
|
static HashSet<RenderTarget> grabbedTextures;
|
|
|
|
/// <summary>
|
|
/// In pixel size, where are we rendering to?
|
|
/// </summary>
|
|
public static Rect Viewport
|
|
{
|
|
get => Context.GetViewport().Rect;
|
|
set => Context.SetViewport( value );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Access to the current render context's attributes. These will be used
|
|
/// to set attributes in materials/shaders. This is cleared at the end of the render block.
|
|
/// </summary>
|
|
public static RenderAttributes Attributes => _state.attributes;
|
|
|
|
|
|
/// <summary>
|
|
/// Access to the current frame's attributes.
|
|
/// These will live until the end of the frame.
|
|
/// </summary>
|
|
internal static RenderAttributes FrameAttributes => _state.frameAttributes;
|
|
|
|
/// <summary>
|
|
/// The camera transform of the currently rendering view
|
|
/// </summary>
|
|
public static Transform CameraTransform => _state.cameraTransform;
|
|
|
|
/// <summary>
|
|
/// The camera position of the currently rendering view
|
|
/// </summary>
|
|
public static Vector3 CameraPosition => CameraTransform.Position;
|
|
|
|
/// <summary>
|
|
/// The camera rotation of the currently rendering view
|
|
/// </summary>
|
|
public static Rotation CameraRotation => CameraTransform.Rotation;
|
|
|
|
|
|
/// <summary>
|
|
/// The field of view of the currently rendering camera view, in degrees.
|
|
/// </summary>
|
|
public static float FieldOfView => _state.sceneView.GetFrustum().GetCameraFOV();
|
|
|
|
/// <summary>
|
|
/// The frustum of the currently rendering camera view.
|
|
/// </summary>
|
|
public static Frustum Frustum
|
|
{
|
|
get
|
|
{
|
|
AssertRenderBlock();
|
|
|
|
var cf = _state.sceneView.GetFrustum();
|
|
|
|
// Extract planes from native CFrustum
|
|
// Plane indices: RIGHT=0, LEFT=1, TOP=2, BOTTOM=3, NEAR=4, FAR=5
|
|
cf.GetPlane( 0, out var rn, out var rd );
|
|
cf.GetPlane( 1, out var ln, out var ld );
|
|
cf.GetPlane( 2, out var tn, out var td );
|
|
cf.GetPlane( 3, out var bn, out var bd );
|
|
cf.GetPlane( 4, out var nn, out var nd );
|
|
cf.GetPlane( 5, out var fn, out var fd );
|
|
|
|
return new Frustum(
|
|
right: new Plane( ln, ld ),
|
|
left: new Plane( ln, rd ),
|
|
top: new Plane( tn, td ),
|
|
bottom: new Plane( bn, bd ),
|
|
near: new Plane( nn, nd ),
|
|
far: new Plane( fn, fd )
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
internal static int RenderMultiSampleToNum( RenderMultisampleType msaaLevel )
|
|
{
|
|
switch ( msaaLevel )
|
|
{
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_NONE:
|
|
return 1;
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_2X:
|
|
return 2;
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_4X:
|
|
return 4;
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_6X:
|
|
return 6;
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_8X:
|
|
return 8;
|
|
case RenderMultisampleType.RENDER_MULTISAMPLE_16X:
|
|
return 16;
|
|
default:
|
|
throw new System.Exception( "Unknown multisample amount" );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a scope where Graphics is safe to use.
|
|
/// </summary>
|
|
internal ref struct Scope
|
|
{
|
|
RenderState _previous;
|
|
|
|
public Scope( in ManagedRenderSetup_t setup )
|
|
{
|
|
_previous = _state;
|
|
|
|
_state = new RenderState();
|
|
|
|
var frustum = setup.sceneView.GetFrustum();
|
|
|
|
_state.active = true;
|
|
_state.sceneLayer = setup.sceneLayer;
|
|
_state.sceneView = setup.sceneView;
|
|
_state.renderContext = setup.renderContext;
|
|
_state.layerType = setup.sceneLayer.LayerEnum;
|
|
_state.colorFormat = setup.colorImageFormat;
|
|
_state.msaaLevel = setup.msaaLevel.FromEngine();
|
|
_state.cameraTransform = new Transform( frustum.GetCameraPosition(), frustum.GetCameraAngles() );
|
|
_state.stats = setup.stats;
|
|
|
|
_state.attributes = ObjectPool<RenderAttributes>.Get();
|
|
_state.attributes.Set( setup.renderContext.GetAttributesPtrForModify() );
|
|
|
|
_state.frameAttributes = ObjectPool<RenderAttributes>.Get();
|
|
_state.frameAttributes.Set( setup.sceneView.GetRenderAttributesPtr() );
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if ( _state.attributes is not null )
|
|
{
|
|
_state.attributes.Set( default( CRenderAttributes ) );
|
|
ObjectPool<RenderAttributes>.Return( _state.attributes );
|
|
}
|
|
|
|
if ( _state.frameAttributes is not null )
|
|
{
|
|
_state.frameAttributes.Set( default( CRenderAttributes ) );
|
|
ObjectPool<RenderAttributes>.Return( _state.frameAttributes );
|
|
}
|
|
|
|
_state = _previous;
|
|
|
|
if ( grabbedTextures is not null )
|
|
{
|
|
// return to the pool
|
|
foreach ( var tex in grabbedTextures )
|
|
{
|
|
tex.Dispose();
|
|
}
|
|
grabbedTextures.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
[MethodImpl( MethodImplOptions.AggressiveInlining )]
|
|
internal static void AssertRenderBlock()
|
|
{
|
|
if ( !IsActive )
|
|
throw new System.Exception( "Tried to render outside of rendering block" );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Setup the lighting attributes for this current object. Place them in the targetAttributes
|
|
/// </summary>
|
|
public static void SetupLighting( SceneObject obj, RenderAttributes targetAttributes = null )
|
|
{
|
|
targetAttributes ??= Attributes;
|
|
|
|
Assert.NotNull( targetAttributes );
|
|
Assert.IsValid( obj );
|
|
|
|
NativeEngine.CSceneSystem.SetupPerObjectLighting( targetAttributes.Get(), obj, SceneLayer );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grabs the current viewport's color texture and stores it in targetName on renderAttributes.
|
|
/// </summary>
|
|
public static RenderTarget GrabFrameTexture( string targetName = "FrameTexture", RenderAttributes renderAttributes = null, DownsampleMethod downsampleMethod = DownsampleMethod.None )
|
|
{
|
|
renderAttributes ??= Attributes;
|
|
|
|
AssertRenderBlock();
|
|
|
|
bool withMips = downsampleMethod != DownsampleMethod.None;
|
|
var numMips = withMips ? (int)Math.Log2( Math.Max( Viewport.Width, Viewport.Height ) ) : 1;
|
|
|
|
// Grab a new one - which may very well be the one we just returned
|
|
var frameTexture = RenderTarget.GetTemporary( 1, ImageFormat.Default, ImageFormat.None, msaa: MultisampleAmount.MultisampleNone, numMips: numMips, targetName );
|
|
|
|
RenderTools.ResolveFrameBuffer( Context, frameTexture.ColorTarget.native, Viewport );
|
|
|
|
// Generate a mip chain if we want one
|
|
if ( withMips ) GenerateMipMaps( frameTexture.ColorTarget, downsampleMethod );
|
|
|
|
grabbedTextures ??= new();
|
|
grabbedTextures.Add( frameTexture );
|
|
|
|
renderAttributes.Set( targetName, frameTexture.ColorTarget );
|
|
|
|
return frameTexture;
|
|
}
|
|
|
|
[Obsolete( "Use GrabFrameTexture with DownsampleMethod instead" )]
|
|
public static void GrabFrameTexture( string targetName, RenderAttributes renderAttributes, bool withMips )
|
|
{
|
|
GrabFrameTexture( targetName, renderAttributes, withMips ? DownsampleMethod.GaussianBlur : DownsampleMethod.None );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Grabs the current depth texture and stores it in targetName on renderAttributes.
|
|
/// </summary>
|
|
public static RenderTarget GrabDepthTexture( string targetName = "DepthTexture", RenderAttributes renderAttributes = null )
|
|
{
|
|
renderAttributes ??= Attributes;
|
|
|
|
AssertRenderBlock();
|
|
|
|
// Grab a new one - which may very well be the one we just returned
|
|
var grabbedTexture = RenderTarget.GetTemporary( 1, ImageFormat.None, ImageFormat.Default, msaa: MultisampleAmount.MultisampleScreen, targetName: targetName );
|
|
|
|
RenderTools.ResolveDepthBuffer( Context, grabbedTexture.DepthTarget.native, Viewport );
|
|
|
|
grabbedTextures ??= new();
|
|
grabbedTextures.Add( grabbedTexture );
|
|
|
|
renderAttributes.Set( targetName, grabbedTexture.DepthTarget );
|
|
|
|
return grabbedTexture;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Get or set the current render target. Setting this will bind the render target and change the viewport to match it.
|
|
/// </summary>
|
|
public static RenderTarget RenderTarget
|
|
{
|
|
get => _state.renderTarget;
|
|
set
|
|
{
|
|
if ( _state.renderTarget == value )
|
|
return;
|
|
|
|
// Going from default render target to custom render target
|
|
if ( _state.renderTarget == null )
|
|
{
|
|
var viewport = Context.GetViewport();
|
|
// Save off min/max Z values here, so we can restore them later.
|
|
_state.defaultMinZ = viewport.MinZ;
|
|
_state.defaultMaxZ = viewport.MaxZ;
|
|
}
|
|
|
|
_state.renderTarget = value;
|
|
|
|
// Resetting to default render target
|
|
if ( _state.renderTarget == null )
|
|
{
|
|
// alex: if we don't restore min/max Z values properly when setting back to
|
|
// the default render target, we get a lot of weird depth issues.
|
|
// This mainly only applies to things like worldpanels when we render filtered
|
|
// elements, but could probably happen in other places too?
|
|
var viewport = new RenderViewport( SceneLayer.m_viewport.Rect, _state.defaultMinZ, _state.defaultMaxZ );
|
|
Context.SetViewport( viewport );
|
|
Context.RestoreRenderTargets( SceneLayer );
|
|
return;
|
|
}
|
|
|
|
Context.BindRenderTargets( _state.renderTarget.ColorTarget?.native ?? default, _state.renderTarget.DepthTarget?.native ?? default, SceneLayer );
|
|
Viewport = new Rect( 0, 0, _state.renderTarget.Width, _state.renderTarget.Height );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the current drawing context to given color.
|
|
/// </summary>
|
|
/// <param name="color">Color to clear to.</param>
|
|
/// <param name="clearColor">Whether to clear the color buffer at all.</param>
|
|
/// <param name="clearDepth">Whether to clear the depth buffer.</param>
|
|
/// <param name="clearStencil">Whether to clear the stencil buffer.</param>
|
|
public static void Clear( Color color, bool clearColor = true, bool clearDepth = true, bool clearStencil = true )
|
|
{
|
|
Context.Clear( color, clearColor, clearDepth, clearStencil );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the current drawing context to given color.
|
|
/// </summary>
|
|
/// <param name="clearColor">Whether to clear the color buffer to transparent color.</param>
|
|
/// <param name="clearDepth">Whether to clear the depth buffer.</param>
|
|
public static void Clear( bool clearColor = true, bool clearDepth = true )
|
|
{
|
|
Clear( Color.Transparent, clearColor, clearDepth, false );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Render this camera to the specified texture target
|
|
/// </summary>
|
|
[Obsolete( "Use CameraComponent.RenderToTexture" )]
|
|
public static bool RenderToTexture( SceneCamera camera, Texture target )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies pixel data from one texture to another on the GPU.
|
|
/// This does not automatically resize or scale the texture, format and size should be equal.
|
|
/// </summary>
|
|
public static void CopyTexture( Texture srcTexture, Texture dstTexture )
|
|
{
|
|
if ( srcTexture == null ) throw new ArgumentNullException( nameof( srcTexture ) );
|
|
if ( dstTexture == null ) throw new ArgumentNullException( nameof( dstTexture ) );
|
|
if ( srcTexture.ImageFormat != dstTexture.ImageFormat ) throw new ArgumentException( "Source and destination texture format must match!" );
|
|
if ( srcTexture.Size != dstTexture.Size ) throw new ArgumentException( "Source and destination texture size must match!" );
|
|
|
|
RenderTools.CopyTexture( Context, srcTexture.native, dstTexture.native, default, 0, 0, 0, 0, 0, 0 );
|
|
}
|
|
|
|
[Obsolete( "Use the CopyTexture overload without 'srcMipLevels' and 'dstMipLevels' parameters instead." )]
|
|
public static void CopyTexture( Texture srcTexture, Texture dstTexture, int srcMipSlice = 0, int srcArraySlice = 0, int srcMipLevels = 1, int dstMipSlice = 0, int dstArraySlice = 0, int dstMipLevels = 1 )
|
|
{
|
|
CopyTexture( srcTexture, dstTexture, srcMipSlice, srcArraySlice, dstMipSlice, dstArraySlice );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies pixel data from one texture to another on the GPU.
|
|
/// This does not automatically resize or scale the texture, format and size should be equal.
|
|
/// This one lets you copy to/from arrays / specific mips.
|
|
/// </summary>
|
|
public static void CopyTexture( Texture srcTexture, Texture dstTexture, int srcMipSlice = 0, int srcArraySlice = 0, int dstMipSlice = 0, int dstArraySlice = 0 )
|
|
{
|
|
if ( srcTexture == null ) throw new ArgumentNullException( nameof( srcTexture ) );
|
|
if ( dstTexture == null ) throw new ArgumentNullException( nameof( dstTexture ) );
|
|
|
|
if ( srcTexture.ImageFormat != dstTexture.ImageFormat && !srcTexture.ImageFormat.IsDepthFormat() ) throw new ArgumentException( "Source and destination texture format must match!" );
|
|
|
|
if ( srcMipSlice < 0 || srcMipSlice >= srcTexture.Mips ) throw new ArgumentException( $"{nameof( srcMipSlice )} out of bounds" );
|
|
if ( dstMipSlice < 0 || dstMipSlice >= dstTexture.Mips ) throw new ArgumentException( $"{nameof( dstMipSlice )} out of bounds" );
|
|
|
|
var srcDesc = srcTexture.Desc;
|
|
var dstDesc = dstTexture.Desc;
|
|
|
|
if ( srcArraySlice < 0 || srcArraySlice >= srcDesc.ArrayCount ) throw new ArgumentException( $"{nameof( srcArraySlice )} out of bounds" );
|
|
if ( dstArraySlice < 0 || dstArraySlice >= dstDesc.ArrayCount ) throw new ArgumentException( $"{nameof( dstArraySlice )} out of bounds" );
|
|
|
|
var srcMipWidth = Math.Max( 1, srcTexture.Width >> srcMipSlice );
|
|
var srcMipHeight = Math.Max( 1, srcTexture.Height >> srcMipSlice );
|
|
var dstMipWidth = Math.Max( 1, dstTexture.Width >> dstMipSlice );
|
|
var dstMipHeight = Math.Max( 1, dstTexture.Height >> dstMipSlice );
|
|
|
|
if ( srcMipWidth != dstMipWidth || srcMipHeight != dstMipHeight )
|
|
throw new ArgumentException( "Source and destination texture mip level sizes must match!" );
|
|
|
|
RenderTools.CopyTexture( Context, srcTexture.native, dstTexture.native, default, 0, 0,
|
|
(uint)srcMipSlice, (uint)srcArraySlice,
|
|
(uint)dstMipSlice, (uint)dstArraySlice );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forces the GPU to flush all pending commands and wait for completion.
|
|
/// Useful when you need to ensure GPU work is finished before proceeding.
|
|
/// Can be called outside of a render block.
|
|
/// </summary>
|
|
public static void FlushGPU()
|
|
{
|
|
if ( Application.IsHeadless )
|
|
return;
|
|
|
|
g_pRenderDevice.ForceFlushGPU( default );
|
|
}
|
|
}
|