private void RemoveRange(ulong gpuVa, ulong size) { int index = BinarySearch(gpuVa + size - 1); if (index < 0) { index = ~index; } if (index >= _items.Count || !InRange(gpuVa, size, _items[index].Address)) { return; } int count = 1; while (index > 0 && InRange(gpuVa, size, _items[index - 1].Address)) { index--; count++; } // Notify the removed counter events that their result should no longer be written out. for (int i = 0; i < count; i++) { ICounterEvent evt = _items[index + i].Event; if (evt != null) { evt.Invalid = true; } } _items.RemoveRange(index, count); }
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { if (value is CounterQueueEvent) { // Compare an event and a constant value. CounterQueueEvent evt = (CounterQueueEvent)value; // Easy host conditional rendering when the check matches what GL can do: // - Event is of type samples passed. // - Result is not a combination of multiple queries. // - Comparing against 0. // - Event has not already been flushed. if (evt.Disposed) { // If the event has been flushed, then just use the values on the CPU. // The query object may already be repurposed for another draw (eg. begin + end). return(false); } if (compare == 0 && evt.Type == QueryTarget.SamplesPassed && evt.ClearCounter) { GL.BeginConditionalRender(evt.Query, isEqual ? ConditionalRenderType.QueryNoWaitInverted : ConditionalRenderType.QueryNoWait); return(true); } } // The GPU will flush the queries to CPU and evaluate the condition there instead. GL.Flush(); // The thread will be stalled manually flushing the counter, so flush GL commands now. return(false); }
/// <summary> /// Checks if the counter at a given GPU memory address passes a specified equality comparison. /// </summary> /// <param name="gpuVa">GPU virtual address</param> /// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param> /// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns> private ConditionalRenderEnabled CounterCompare(ulong gpuVa, bool isEqual) { ICounterEvent evt = FindEvent(gpuVa); ICounterEvent evt2 = FindEvent(gpuVa + 16); if (evt == null && evt2 == null) { return(ConditionalRenderEnabled.False); } if (_context.Renderer.Pipeline.TryHostConditionalRendering( evt ?? (object)_context.MemoryAccessor.ReadUInt64(gpuVa), evt2 ?? (object)_context.MemoryAccessor.ReadUInt64(gpuVa + 16), isEqual)) { return(ConditionalRenderEnabled.Host); } else { evt?.Flush(); evt2?.Flush(); ulong x = _context.MemoryAccessor.ReadUInt64(gpuVa); ulong y = _context.MemoryAccessor.ReadUInt64(gpuVa + 16); return((isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False); } }
/// <summary> /// Writes a GPU counter to guest memory. /// This also writes the current timestamp value. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="type">Counter to be written to memory</param> private void ReportCounter(GpuState state, ReportCounterType type) { CounterData counterData = new CounterData(); var rs = state.Get <SemaphoreState>(MethodOffset.ReportState); ulong gpuVa = rs.Address.Pack(); ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); if (GraphicsConfig.FastGpuTime) { // Divide by some amount to report time as if operations were performed faster than they really are. // This can prevent some games from switching to a lower resolution because rendering is too slow. ticks /= 256; } ICounterEvent counter = null; EventHandler <ulong> resultHandler = (object evt, ulong result) => { counterData.Counter = result; counterData.Timestamp = ticks; Span <CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1); Span <byte> data = MemoryMarshal.Cast <CounterData, byte>(counterDataSpan); if (counter?.Invalid != true) { _context.MemoryAccessor.Write(gpuVa, data); } }; switch (type) { case ReportCounterType.Zero: resultHandler(null, 0); break; case ReportCounterType.SamplesPassed: counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler); break; case ReportCounterType.PrimitivesGenerated: counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler); break; } _counterCache.AddOrUpdate(gpuVa, counter); }
/// <summary> /// Writes a GPU counter to guest memory. /// This also writes the current timestamp value. /// </summary> /// <param name="type">Counter to be written to memory</param> private void ReportCounter(ReportCounterType type) { ulong gpuVa = _state.State.SemaphoreAddress.Pack(); ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); if (GraphicsConfig.FastGpuTime) { // Divide by some amount to report time as if operations were performed faster than they really are. // This can prevent some games from switching to a lower resolution because rendering is too slow. ticks /= 256; } ICounterEvent counter = null; void resultHandler(object evt, ulong result) { CounterData counterData = new CounterData { Counter = result, Timestamp = ticks }; if (counter?.Invalid != true) { _channel.MemoryManager.Write(gpuVa, counterData); } } switch (type) { case ReportCounterType.Zero: resultHandler(null, 0); break; case ReportCounterType.SamplesPassed: counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler); break; case ReportCounterType.PrimitivesGenerated: counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler); break; } _channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter); }
/// <summary> /// Adds a new counter to the counter cache, or updates a existing one. /// </summary> /// <param name="gpuVa">GPU virtual address where the counter will be written in memory</param> public void AddOrUpdate(ulong gpuVa, ICounterEvent evt) { int index = BinarySearch(gpuVa); CounterEntry entry = new CounterEntry(gpuVa, evt); if (index < 0) { _items.Insert(~index, entry); } else { _items[index] = entry; } }
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { // Compare an event and a constant value. if (value is CounterQueueEvent evt) { // Easy host conditional rendering when the check matches what GL can do: // - Event is of type samples passed. // - Result is not a combination of multiple queries. // - Comparing against 0. // - Event has not already been flushed. if (compare == 0 && evt.Type == CounterType.SamplesPassed && evt.ClearCounter) { if (!value.ReserveForHostAccess()) { // If the event has been flushed, then just use the values on the CPU. // The query object may already be repurposed for another draw (eg. begin + end). return(false); } if (Gd.Capabilities.SupportsConditionalRendering) { var buffer = evt.GetBuffer().Get(Cbs, 0, sizeof(long)).Value; var flags = isEqual ? ConditionalRenderingFlagsEXT.ConditionalRenderingInvertedBitExt : 0; var conditionalRenderingBeginInfo = new ConditionalRenderingBeginInfoEXT() { SType = StructureType.ConditionalRenderingBeginInfoExt, Buffer = buffer, Flags = flags }; // Gd.ConditionalRenderingApi.CmdBeginConditionalRendering(CommandBuffer, conditionalRenderingBeginInfo); } _activeConditionalRender = evt; return(true); } } // The GPU will flush the queries to CPU and evaluate the condition there instead. FlushPendingQuery(); // The thread will be stalled manually flushing the counter, so flush commands now. return(false); }
/// <summary> /// Checks if the counter value at a given GPU memory address is non-zero. /// </summary> /// <param name="gpuVa">GPU virtual address of the counter value</param> /// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns> private ConditionalRenderEnabled CounterNonZero(ulong gpuVa) { ICounterEvent evt = _counterCache.FindEvent(gpuVa); if (evt == null) { return(ConditionalRenderEnabled.False); } if (_context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false)) { return(ConditionalRenderEnabled.Host); } else { evt.Flush(); return((_context.MemoryAccessor.ReadUInt64(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False); } }
/// <summary> /// Writes a GPU counter to guest memory. /// This also writes the current timestamp value. /// </summary> /// <param name="type">Counter to be written to memory</param> private void ReportCounter(ReportCounterType type) { ulong gpuVa = _state.State.SemaphoreAddress.Pack(); ulong ticks = _context.GetTimestamp(); ICounterEvent counter = null; void resultHandler(object evt, ulong result) { CounterData counterData = new CounterData { Counter = result, Timestamp = ticks }; if (counter?.Invalid != true) { _channel.MemoryManager.Write(gpuVa, counterData); } } switch (type) { case ReportCounterType.Zero: resultHandler(null, 0); break; case ReportCounterType.SamplesPassed: counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false); break; case ReportCounterType.PrimitivesGenerated: counter = _context.Renderer.ReportCounter(CounterType.PrimitivesGenerated, resultHandler, false); break; case ReportCounterType.TransformFeedbackPrimitivesWritten: counter = _context.Renderer.ReportCounter(CounterType.TransformFeedbackPrimitivesWritten, resultHandler, false); break; } _channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter); }
/// <summary> /// Checks if the counter value at a given GPU memory address is non-zero. /// </summary> /// <param name="context">GPU context</param> /// <param name="memoryManager">Memory manager bound to the channel currently executing</param> /// <param name="gpuVa">GPU virtual address of the counter value</param> /// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns> private static ConditionalRenderEnabled CounterNonZero(GpuContext context, MemoryManager memoryManager, ulong gpuVa) { ICounterEvent evt = memoryManager.CounterCache.FindEvent(gpuVa); if (evt == null) { return(ConditionalRenderEnabled.False); } if (context.Renderer.Pipeline.TryHostConditionalRendering(evt, 0L, false)) { return(ConditionalRenderEnabled.Host); } else { evt.Flush(); return((memoryManager.Read <ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False); } }
/// <summary> /// Checks if the counter at a given GPU memory address passes a specified equality comparison. /// </summary> /// <param name="gpuVa">GPU virtual address</param> /// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param> /// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns> private ConditionalRenderEnabled CounterCompare(ulong gpuVa, bool isEqual) { ICounterEvent evt = FindEvent(gpuVa); ICounterEvent evt2 = FindEvent(gpuVa + 16); if (evt == null && evt2 == null) { return(ConditionalRenderEnabled.False); } bool useHost; if (evt != null && evt2 == null) { useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryManager.Read <ulong>(gpuVa + 16), isEqual); } else if (evt == null && evt2 != null) { useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryManager.Read <ulong>(gpuVa), isEqual); } else { useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual); } if (useHost) { return(ConditionalRenderEnabled.Host); } else { evt?.Flush(); evt2?.Flush(); ulong x = _context.MemoryManager.Read <ulong>(gpuVa); ulong y = _context.MemoryManager.Read <ulong>(gpuVa + 16); return((isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False); } }
/// <summary> /// Checks if the counter at a given GPU memory address passes a specified equality comparison. /// </summary> /// <param name="context">GPU context</param> /// <param name="memoryManager">Memory manager bound to the channel currently executing</param> /// <param name="gpuVa">GPU virtual address</param> /// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param> /// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns> private static ConditionalRenderEnabled CounterCompare(GpuContext context, MemoryManager memoryManager, ulong gpuVa, bool isEqual) { ICounterEvent evt = FindEvent(memoryManager.CounterCache, gpuVa); ICounterEvent evt2 = FindEvent(memoryManager.CounterCache, gpuVa + 16); bool useHost; if (evt != null && evt2 == null) { useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, memoryManager.Read <ulong>(gpuVa + 16), isEqual); } else if (evt == null && evt2 != null) { useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt2, memoryManager.Read <ulong>(gpuVa), isEqual); } else if (evt != null && evt2 != null) { useHost = context.Renderer.Pipeline.TryHostConditionalRendering(evt, evt2, isEqual); } else { useHost = false; } if (useHost) { return(ConditionalRenderEnabled.Host); } else { evt?.Flush(); evt2?.Flush(); ulong x = memoryManager.Read <ulong>(gpuVa); ulong y = memoryManager.Read <ulong>(gpuVa + 16); return((isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False); } }
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { var evt = value as ThreadedCounterEvent; if (evt != null) { if (compare == 0 && evt.Type == CounterType.SamplesPassed && evt.ClearCounter) { if (!evt.ReserveForHostAccess()) { return(false); } _renderer.New <TryHostConditionalRenderingCommand>().Set(Ref(evt), compare, isEqual); _renderer.QueueCommand(); return(true); } } _renderer.New <TryHostConditionalRenderingFlushCommand>().Set(Ref(evt), Ref <ThreadedCounterEvent>(null), isEqual); _renderer.QueueCommand(); return(false); }
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { _renderer.New <TryHostConditionalRenderingFlushCommand>().Set(Ref(value as ThreadedCounterEvent), Ref(compare as ThreadedCounterEvent), isEqual); _renderer.QueueCommand(); return(false); }
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { GL.Flush(); // The GPU thread will be stalled manually flushing the counter, so flush GL commands now. return(false); // We don't currently have a way to compare two counters for conditional rendering. }
public CounterEntry(ulong address, ICounterEvent evt) { Address = address; Event = evt; }
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { FlushPendingQuery(); // The thread will be stalled manually flushing the counter, so flush commands now. return(false); }
public void Create(IRenderer renderer, CounterType type, System.EventHandler <ulong> eventHandler, bool hostReserved) { ThreadedHelpers.SpinUntilExchange(ref _createLock, 1, 0); Base = renderer.ReportCounter(type, eventHandler, hostReserved || _reserved); Volatile.Write(ref _createLock, 0); }
public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { return(false); // We don't currently have a way to compare two counters for conditional rendering. }