Beispiel #1
0
 /// <summary>
 /// Register a read/write action to flush for a texture group.
 /// </summary>
 /// <param name="group">The group to register an action for</param>
 public void RegisterAction(TextureGroupHandle group)
 {
     foreach (CpuRegionHandle handle in group.Handles)
     {
         handle.RegisterAction((address, size) => FlushAction(group, address, size));
     }
 }
Beispiel #2
0
        /// <summary>
        /// Check and optionally consume the dirty flags for a given texture.
        /// The state is shared between views of the same layers and levels.
        /// </summary>
        /// <param name="texture">The texture being used</param>
        /// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
        /// <returns>True if a flag was dirty, false otherwise</returns>
        public bool CheckDirty(Texture texture, bool consume)
        {
            bool dirty = false;

            EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
            {
                for (int i = 0; i < regionCount; i++)
                {
                    TextureGroupHandle group = _handles[baseHandle + i];

                    foreach (CpuRegionHandle handle in group.Handles)
                    {
                        if (handle.Dirty)
                        {
                            if (consume)
                            {
                                handle.Reprotect();
                            }

                            dirty = true;
                        }
                    }
                }
            });

            return(dirty);
        }
        /// <summary>
        /// Signal that a copy dependent texture has been modified, and must have its data copied to this one.
        /// </summary>
        /// <param name="copyFrom">The texture handle that must defer a copy to this one</param>
        public void DeferCopy(TextureGroupHandle copyFrom)
        {
            DeferredCopy = copyFrom;

            _group.Storage.SignalGroupDirty();

            foreach (Texture overlap in Overlaps)
            {
                overlap.SignalGroupDirty();
            }
        }
        /// <summary>
        /// Synchronize dependent textures, if any of them have deferred a copy from this texture.
        /// </summary>
        public void SynchronizeDependents()
        {
            foreach (TextureDependency dependency in Dependencies)
            {
                TextureGroupHandle otherHandle = dependency.Other.Handle;

                if (otherHandle.DeferredCopy == this)
                {
                    otherHandle._group.Storage.SynchronizeMemory();
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Synchronize dependent textures, if any of them have deferred a copy from the given texture.
        /// </summary>
        /// <param name="texture">The texture to synchronize dependents of</param>
        public void SynchronizeDependents(Texture texture)
        {
            EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
            {
                for (int i = 0; i < regionCount; i++)
                {
                    TextureGroupHandle group = _handles[baseHandle + i];

                    group.SynchronizeDependents();
                }
            });
        }
Beispiel #6
0
        /// <summary>
        /// A flush has been requested on a tracked region. Find an appropriate view to flush.
        /// </summary>
        /// <param name="handle">The handle this flush action is for</param>
        /// <param name="address">The address of the flushing memory access</param>
        /// <param name="size">The size of the flushing memory access</param>
        public void FlushAction(TextureGroupHandle handle, ulong address, ulong size)
        {
            Storage.ExternalFlush(address, size);

            lock (handle.Overlaps)
            {
                foreach (Texture overlap in handle.Overlaps)
                {
                    overlap.ExternalFlush(address, size);
                }
            }

            handle.Modified = false;
        }
Beispiel #7
0
        /// <summary>
        /// The action to perform when a memory tracking handle is flipped to dirty.
        /// This notifies overlapping textures that the memory needs to be synchronized.
        /// </summary>
        /// <param name="groupHandle">The handle that a dirty flag was set on</param>
        private void DirtyAction(TextureGroupHandle groupHandle)
        {
            // Notify all textures that belong to this handle.

            Storage.SignalGroupDirty();

            lock (groupHandle.Overlaps)
            {
                foreach (Texture overlap in groupHandle.Overlaps)
                {
                    overlap.SignalGroupDirty();
                }
            }
        }
        /// <summary>
        /// Create a copy dependency between this handle, and another.
        /// </summary>
        /// <param name="other">The handle to create a copy dependency to</param>
        /// <param name="copyToOther">True if a copy should be deferred to all of the other handle's dependencies</param>
        public void CreateCopyDependency(TextureGroupHandle other, bool copyToOther = false)
        {
            // Does this dependency already exist?
            foreach (TextureDependency existing in Dependencies)
            {
                if (existing.Other.Handle == other)
                {
                    // Do not need to create it again. May need to set the dirty flag.
                    return;
                }
            }

            _group.HasCopyDependencies       = true;
            other._group.HasCopyDependencies = true;

            TextureDependency dependency      = new TextureDependency(this);
            TextureDependency otherDependency = new TextureDependency(other);

            dependency.Other      = otherDependency;
            otherDependency.Other = dependency;

            Dependencies.Add(dependency);
            other.Dependencies.Add(otherDependency);

            // Recursively create dependency:
            // All of this handle's dependencies must depend on the other.
            foreach (TextureDependency existing in Dependencies.ToArray())
            {
                if (existing != dependency && existing.Other.Handle != other)
                {
                    existing.Other.Handle.CreateCopyDependency(other);
                }
            }

            // All of the other handle's dependencies must depend on this.
            foreach (TextureDependency existing in other.Dependencies.ToArray())
            {
                if (existing != otherDependency && existing.Other.Handle != this)
                {
                    existing.Other.Handle.CreateCopyDependency(this);

                    if (copyToOther)
                    {
                        existing.Other.Handle.DeferCopy(this);
                    }
                }
            }
        }
        /// <summary>
        /// Inherit modified flags and dependencies from another texture handle.
        /// </summary>
        /// <param name="old">The texture handle to inherit from</param>
        public void Inherit(TextureGroupHandle old)
        {
            Modified |= old.Modified;

            foreach (TextureDependency dependency in old.Dependencies.ToArray())
            {
                CreateCopyDependency(dependency.Other.Handle);

                if (dependency.Other.Handle.DeferredCopy == old)
                {
                    dependency.Other.Handle.DeferredCopy = this;
                }
            }

            DeferredCopy = old.DeferredCopy;
        }
Beispiel #10
0
        /// <summary>
        /// Generate a TextureGroupHandle covering a specified range of views.
        /// </summary>
        /// <param name="viewStart">The start view of the handle</param>
        /// <param name="views">The number of views to cover</param>
        /// <returns>A TextureGroupHandle covering the given views</returns>
        private TextureGroupHandle GenerateHandles(int viewStart, int views)
        {
            int offset    = _allOffsets[viewStart];
            int endOffset = (viewStart + views == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[viewStart + views];
            int size      = endOffset - offset;

            var result = new List <CpuRegionHandle>();

            for (int i = 0; i < TextureRange.Count; i++)
            {
                MemoryRange item         = TextureRange.GetSubRange(i);
                int         subRangeSize = (int)item.Size;

                int sliceStart = Math.Clamp(offset, 0, subRangeSize);
                int sliceEnd   = Math.Clamp(endOffset, 0, subRangeSize);

                if (sliceStart != sliceEnd)
                {
                    result.Add(GenerateHandle(item.Address + (ulong)sliceStart, (ulong)(sliceEnd - sliceStart)));
                }

                offset    -= subRangeSize;
                endOffset -= subRangeSize;

                if (endOffset <= 0)
                {
                    break;
                }
            }

            (int firstLayer, int firstLevel) = GetLayerLevelForView(viewStart);

            if (_hasLayerViews && _hasMipViews)
            {
                size = _sliceSizes[firstLevel];
            }

            var groupHandle = new TextureGroupHandle(this, _allOffsets[viewStart], (ulong)size, _views, firstLayer, firstLevel, result.ToArray());

            foreach (CpuRegionHandle handle in result)
            {
                handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
            }

            return(groupHandle);
        }
Beispiel #11
0
        /// <summary>
        /// Signal that a texture in the group is actively bound, or has been unbound by the GPU.
        /// </summary>
        /// <param name="texture">The texture that has been modified</param>
        /// <param name="bound">True if this texture is being bound, false if unbound</param>
        /// <param name="registerAction">True if the flushing read action should be registered, false otherwise</param>
        public void SignalModifying(Texture texture, bool bound, bool registerAction)
        {
            EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
            {
                for (int i = 0; i < regionCount; i++)
                {
                    TextureGroupHandle group = _handles[baseHandle + i];

                    group.SignalModifying(bound);

                    if (registerAction)
                    {
                        RegisterAction(group);
                    }
                }
            });
        }
Beispiel #12
0
        /// <summary>
        /// Perform a copy from the provided handle to this one, or perform a deferred copy if none is provided.
        /// </summary>
        /// <param name="fromHandle">The handle to copy from. If not provided, this method will copy from and clear the deferred copy instead</param>
        /// <returns>True if the copy was performed, false otherwise</returns>
        public bool Copy(TextureGroupHandle fromHandle = null)
        {
            bool result = false;

            if (fromHandle == null)
            {
                fromHandle = DeferredCopy;

                if (fromHandle != null && fromHandle._bindCount == 0)
                {
                    // Repeat the copy in future if the bind count is greater than 0.
                    DeferredCopy = null;
                }
            }

            if (fromHandle != null)
            {
                // If the copy texture is dirty, do not copy. Its data no longer matters, and this handle should also be dirty.
                if (!fromHandle.CheckDirty())
                {
                    Texture from = fromHandle._group.Storage;
                    Texture to   = _group.Storage;

                    if (from.ScaleFactor != to.ScaleFactor)
                    {
                        to.PropagateScale(from);
                    }

                    from.HostTexture.CopyTo(
                        to.HostTexture,
                        fromHandle._firstLayer,
                        _firstLayer,
                        fromHandle._firstLevel,
                        _firstLevel);

                    Modified = true;

                    _group.RegisterAction(this);

                    result = true;
                }
            }

            return(result);
        }
Beispiel #13
0
        /// <summary>
        /// Create a copy dependency between this texture group, and a texture at a given layer/level offset.
        /// </summary>
        /// <param name="other">The view compatible texture to create a dependency to</param>
        /// <param name="firstLayer">The base layer of the given texture relative to the storage</param>
        /// <param name="firstLevel">The base level of the given texture relative to the storage</param>
        /// <param name="copyTo">True if this texture is first copied to the given one, false for the opposite direction</param>
        public void CreateCopyDependency(Texture other, int firstLayer, int firstLevel, bool copyTo)
        {
            TextureGroup otherGroup = other.Group;

            EnsureFullSubdivision();
            otherGroup.EnsureFullSubdivision();

            // Get the location of each texture within its storage, so we can find the handles to apply the dependency to.
            // This can consist of multiple disjoint regions, for example if this is a mip slice of an array texture.

            var targetRange = new List <(int BaseHandle, int RegionCount)>();
            var otherRange  = new List <(int BaseHandle, int RegionCount)>();

            EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, split) => targetRange.Add((baseHandle, regionCount)));
            otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, split) => otherRange.Add((baseHandle, regionCount)));

            int targetIndex = 0;
            int otherIndex  = 0;

            (int Handle, int RegionCount)targetRegion = (0, 0);
            (int Handle, int RegionCount)otherRegion  = (0, 0);

            while (true)
            {
                if (targetRegion.RegionCount == 0)
                {
                    if (targetIndex >= targetRange.Count)
                    {
                        break;
                    }

                    targetRegion = targetRange[targetIndex++];
                }

                if (otherRegion.RegionCount == 0)
                {
                    if (otherIndex >= otherRange.Count)
                    {
                        break;
                    }

                    otherRegion = otherRange[otherIndex++];
                }

                TextureGroupHandle handle      = _handles[targetRegion.Handle++];
                TextureGroupHandle otherHandle = other.Group._handles[otherRegion.Handle++];

                targetRegion.RegionCount--;
                otherRegion.RegionCount--;

                handle.CreateCopyDependency(otherHandle, copyTo);

                // If "copyTo" is true, this texture must copy to the other.
                // Otherwise, it must copy to this texture.

                if (copyTo)
                {
                    otherHandle.Copy(handle);
                }
                else
                {
                    handle.Copy(otherHandle);
                }
            }
        }
Beispiel #14
0
        /// <summary>
        /// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
        /// </summary>
        private void RecalculateHandleRegions()
        {
            TextureGroupHandle[] handles;

            if (!(_hasMipViews || _hasLayerViews))
            {
                // Single dirty region.
                var cpuRegionHandles = new CpuRegionHandle[TextureRange.Count];

                for (int i = 0; i < TextureRange.Count; i++)
                {
                    var currentRange = TextureRange.GetSubRange(i);
                    cpuRegionHandles[i] = GenerateHandle(currentRange.Address, currentRange.Size);
                }

                var groupHandle = new TextureGroupHandle(this, 0, Storage.Size, _views, 0, 0, cpuRegionHandles);

                foreach (CpuRegionHandle handle in cpuRegionHandles)
                {
                    handle.RegisterDirtyEvent(() => DirtyAction(groupHandle));
                }

                handles = new TextureGroupHandle[] { groupHandle };
            }
            else
            {
                // Get views for the host texture.
                // It's worth noting that either the texture has layer views or mip views when getting to this point, which simplifies the logic a little.
                // Depending on if the texture is 3d, either the mip views imply that layer views are present (2d) or the other way around (3d).
                // This is enforced by the way the texture matched as a view, so we don't need to check.

                int layerHandles = _hasLayerViews ? _layers : 1;
                int levelHandles = _hasMipViews ? _levels : 1;

                int handleIndex = 0;

                if (_is3D)
                {
                    var handlesList = new List <TextureGroupHandle>();

                    for (int i = 0; i < levelHandles; i++)
                    {
                        for (int j = 0; j < layerHandles; j++)
                        {
                            (int viewStart, int views) = Get3DLevelRange(i);
                            viewStart += j;
                            views      = _hasLayerViews ? 1 : views; // A layer view is also a mip view.

                            handlesList.Add(GenerateHandles(viewStart, views));
                        }

                        layerHandles = Math.Max(1, layerHandles >> 1);
                    }

                    handles = handlesList.ToArray();
                }
                else
                {
                    handles = new TextureGroupHandle[layerHandles * levelHandles];

                    for (int i = 0; i < layerHandles; i++)
                    {
                        for (int j = 0; j < levelHandles; j++)
                        {
                            int viewStart = j + i * _levels;
                            int views     = _hasMipViews ? 1 : _levels; // A mip view is also a layer view.

                            handles[handleIndex++] = GenerateHandles(viewStart, views);
                        }
                    }
                }
            }

            ReplaceHandles(handles);
        }
Beispiel #15
0
        /// <summary>
        /// Synchronize memory for a given texture.
        /// If overlapping tracking handles are dirty, fully or partially synchronize the texture data.
        /// </summary>
        /// <param name="texture">The texture being used</param>
        public void SynchronizeMemory(Texture texture)
        {
            EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
            {
                bool dirty       = false;
                bool anyModified = false;
                bool anyUnmapped = false;

                for (int i = 0; i < regionCount; i++)
                {
                    TextureGroupHandle group = _handles[baseHandle + i];

                    bool modified       = group.Modified;
                    bool handleDirty    = false;
                    bool handleModified = false;
                    bool handleUnmapped = false;

                    foreach (CpuRegionHandle handle in group.Handles)
                    {
                        if (handle.Dirty)
                        {
                            handle.Reprotect();
                            handleDirty = true;
                        }
                        else
                        {
                            handleUnmapped |= handle.Unmapped;
                            handleModified |= modified;
                        }
                    }

                    // Evaluate if any copy dependencies need to be fulfilled. A few rules:
                    // If the copy handle needs to be synchronized, prefer our own state.
                    // If we need to be synchronized and there is a copy present, prefer the copy.

                    if (group.NeedsCopy && group.Copy())
                    {
                        anyModified |= true; // The copy target has been modified.
                        handleDirty  = false;
                    }
                    else
                    {
                        anyModified |= handleModified;
                        dirty       |= handleDirty;
                    }

                    anyUnmapped |= handleUnmapped;

                    if (group.NeedsCopy)
                    {
                        // The texture we copied from is still being written to. Copy from it again the next time this texture is used.
                        texture.SignalGroupDirty();
                    }

                    _loadNeeded[baseHandle + i] = handleDirty && !handleUnmapped;
                }

                if (dirty)
                {
                    if (anyUnmapped || (_handles.Length > 1 && (anyModified || split)))
                    {
                        // Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.

                        SynchronizePartial(baseHandle, regionCount);
                    }
                    else
                    {
                        // Full texture invalidation.

                        texture.SynchronizeFull();
                    }
                }
            });
        }
Beispiel #16
0
        /// <summary>
        /// Perform a copy from the provided handle to this one, or perform a deferred copy if none is provided.
        /// </summary>
        /// <param name="context">GPU context to register sync for modified handles</param>
        /// <param name="fromHandle">The handle to copy from. If not provided, this method will copy from and clear the deferred copy instead</param>
        /// <returns>True if the copy was performed, false otherwise</returns>
        public bool Copy(GpuContext context, TextureGroupHandle fromHandle = null)
        {
            bool result     = false;
            bool shouldCopy = false;

            if (fromHandle == null)
            {
                fromHandle = DeferredCopy;

                if (fromHandle != null)
                {
                    // Only copy if the copy texture is still modified.
                    // It will be set as unmodified if new data is written from CPU, as the data previously in the texture will flush.
                    // It will also set as unmodified if a copy is deferred to it.

                    shouldCopy = fromHandle.Modified;

                    if (fromHandle._bindCount == 0)
                    {
                        // Repeat the copy in future if the bind count is greater than 0.
                        DeferredCopy = null;
                    }
                }
            }
            else
            {
                // Copies happen directly when initializing a copy dependency.
                // If dirty, do not copy. Its data no longer matters, and this handle should also be dirty.
                // Also, only direct copy if the data in this handle is not already modified (can be set by copies from modified handles).
                shouldCopy = !fromHandle.CheckDirty() && (fromHandle.Modified || !Modified);
            }

            if (shouldCopy)
            {
                Texture from = fromHandle._group.Storage;
                Texture to   = _group.Storage;

                if (from.ScaleFactor != to.ScaleFactor)
                {
                    to.PropagateScale(from);
                }

                from.HostTexture.CopyTo(
                    to.HostTexture,
                    fromHandle._firstLayer,
                    _firstLayer,
                    fromHandle._firstLevel,
                    _firstLevel);

                if (fromHandle.Modified)
                {
                    Modified = true;

                    RegisterSync(context);
                }

                result = true;
            }

            return(result);
        }