/// <summary> /// Updates storage buffer bindings. /// </summary> private void UpdateStorageBuffers() { for (int stage = 0; stage < CurrentGpMeta.Info.Length; stage++) { ShaderProgramInfo info = CurrentGpMeta.Info[stage]; if (info == null) { continue; } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; ulong sbDescAddress = BufferManager.GetGraphicsUniformBufferAddress(stage, 0); int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; ReadOnlySpan <byte> sbDescriptorData = _context.PhysicalMemory.GetSpan(sbDescAddress, 0x10); SbDescriptor sbDescriptor = MemoryMarshal.Cast <byte, SbDescriptor>(sbDescriptorData)[0]; BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); } } }
/// <summary> /// Updates storage buffer bindings. /// </summary> private void UpdateStorageBuffers() { for (int stage = 0; stage < _currentProgramInfo.Length; stage++) { ShaderProgramInfo info = _currentProgramInfo[stage]; if (info == null) { continue; } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; ulong sbDescAddress = BufferManager.GetGraphicsUniformBufferAddress(stage, 0); int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; SbDescriptor sbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(sbDescAddress); BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); } } }
public ShaderProgram Translate( out ShaderProgramInfo shaderProgramInfo, TranslatorContext nextStage = null, TranslatorContext other = null) { if (nextStage != null) { _config.MergeOutputUserAttributes(nextStage._config.UsedInputAttributes); } FunctionCode[] code = EmitShader(_cfg, _config, initializeOutputs: other == null, out _); if (other != null) { other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes); FunctionCode[] otherCode = EmitShader(other._cfg, other._config, initializeOutputs: true, out int aStart); code = Combine(otherCode, code, aStart); _config.InheritFrom(other._config); } return(Translator.Translate(code, _config, out shaderProgramInfo)); }
/// <summary> /// Creates a new instace of the shader code holder. /// </summary> /// <param name="program">Shader program</param> /// <param name="info">Shader program information</param> /// <param name="code">Maxwell binary shader code</param> /// <param name="code2">Optional binary shader code of the "Vertex A" shader, when combined with "Vertex B"</param> public ShaderCodeHolder(ShaderProgram program, ShaderProgramInfo info, byte[] code, byte[] code2 = null) { Program = program; Info = info; Code = code; Code2 = code2; }
private static ShaderProgram Translate(Operation[] ops, ShaderConfig config, int size) { BasicBlock[] blocks = ControlFlowGraph.MakeCfg(ops); if (blocks.Length > 0) { Dominance.FindDominators(blocks[0], blocks.Length); Dominance.FindDominanceFrontiers(blocks); Ssa.Rename(blocks); Optimizer.RunPass(blocks, config); Lowering.RunPass(blocks, config); } StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(blocks, config); GlslProgram program = GlslGenerator.Generate(sInfo, config); ShaderProgramInfo spInfo = new ShaderProgramInfo( program.CBufferDescriptors, program.SBufferDescriptors, program.TextureDescriptors, program.ImageDescriptors, sInfo.UsesInstanceId); string glslCode = program.Code; return(new ShaderProgram(spInfo, config.Stage, glslCode, size)); }
public static ShaderInfolElement Create(ShaderKeywordInfo keywordInfo, ShaderProgramInfo programinfo, List <PassInformation> passInfoms) { var obj = new ShaderInfolElement(keywordInfo, programinfo, passInfoms); return(obj); }
private void InitShaderElementInfo(ShaderProgramInfo info) { var positionPerf = InitShaderProgramElement("Vertex(Position)", info.positionVertPerf); var varyingPerf = InitShaderProgramElement("Vertex(Varying)", info.varyingVertPerf); var fragPerf = InitShaderProgramElement("Fragment", info.fragPerf); this.resultArea.Add(positionPerf); this.resultArea.Add(varyingPerf); this.resultArea.Add(fragPerf); }
private HostShaderCacheEntry(ShaderProgramInfo programInfo) { Header = new HostShaderCacheEntryHeader(programInfo.CBuffers.Count, programInfo.SBuffers.Count, programInfo.Textures.Count, programInfo.Images.Count, programInfo.UsesInstanceId); CBuffers = programInfo.CBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray(); Textures = programInfo.Textures.ToArray(); Images = programInfo.Images.ToArray(); }
public ShaderProgram Translate(out ShaderProgramInfo shaderProgramInfo, TranslatorContext other = null) { FunctionCode[] code = EmitShader(_cfg, _config); if (other != null) { code = Combine(EmitShader(other._cfg, other._config), code); _config.InheritFrom(other._config); } return(Translator.Translate(code, _config, out shaderProgramInfo)); }
public ShaderProgram Translate(out ShaderProgramInfo shaderProgramInfo, TranslatorContext other = null) { FunctionCode[] code = EmitShader(_cfg, _config); if (other != null) { _config.SetUsedFeature(other._config.UsedFeatures); TextureHandlesForCache.UnionWith(other.TextureHandlesForCache); code = Combine(EmitShader(other._cfg, other._config), code); } return(Translator.Translate(code, _config, out shaderProgramInfo)); }
private HostShaderCacheEntry(ShaderProgramInfo programInfo) { Header = new HostShaderCacheEntryHeader(programInfo.CBuffers.Count, programInfo.SBuffers.Count, programInfo.Textures.Count, programInfo.Images.Count, programInfo.UsesInstanceId, programInfo.UsesRtLayer, programInfo.ClipDistancesWritten, programInfo.FragmentOutputMap); CBuffers = programInfo.CBuffers.ToArray(); SBuffers = programInfo.SBuffers.ToArray(); Textures = programInfo.Textures.ToArray(); Images = programInfo.Images.ToArray(); }
public ShaderProgram Translate(out ShaderProgramInfo shaderProgramInfo) { FunctionCode[] code = EmitShader(_cfg, _config); if (_configA != null) { FunctionCode[] codeA = EmitShader(_cfgA, _configA); _config.SetUsedFeature(_configA.UsedFeatures); code = Combine(codeA, code); } return(Translator.Translate(code, _config, out shaderProgramInfo, SizeA)); }
/// <summary> /// Writes the shader program info into the cache. /// </summary> /// <param name="dataWriter">Cache data writer</param> /// <param name="info">Program info</param> private static void WriteShaderProgramInfo(ref BinarySerializer dataWriter, ShaderProgramInfo info) { if (info == null) { return; } DataShaderInfo dataInfo = new DataShaderInfo(); dataInfo.CBuffersCount = (ushort)info.CBuffers.Count; dataInfo.SBuffersCount = (ushort)info.SBuffers.Count; dataInfo.TexturesCount = (ushort)info.Textures.Count; dataInfo.ImagesCount = (ushort)info.Images.Count; dataInfo.Stage = info.Stage; dataInfo.UsesInstanceId = info.UsesInstanceId; dataInfo.UsesRtLayer = info.UsesRtLayer; dataInfo.ClipDistancesWritten = info.ClipDistancesWritten; dataInfo.FragmentOutputMap = info.FragmentOutputMap; dataWriter.WriteWithMagicAndSize(ref dataInfo, ShdiMagic); for (int index = 0; index < info.CBuffers.Count; index++) { var entry = info.CBuffers[index]; dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic); } for (int index = 0; index < info.SBuffers.Count; index++) { var entry = info.SBuffers[index]; dataWriter.WriteWithMagicAndSize(ref entry, BufdMagic); } for (int index = 0; index < info.Textures.Count; index++) { var entry = info.Textures[index]; dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic); } for (int index = 0; index < info.Images.Count; index++) { var entry = info.Images[index]; dataWriter.WriteWithMagicAndSize(ref entry, TexdMagic); } }
private ShaderInfolElement(ShaderKeywordInfo keywordInfo, ShaderProgramInfo programinfo, List <PassInformation> passInfoms) { var fold = new Foldout(); var str = CreateSummaryString(keywordInfo, passInfoms); fold.text = str; var keywordVe = CreateKeywordInfoElement(keywordInfo); var positionVe = CreateProgramElement("positionVertPerf", programinfo.positionVertPerf); var varyingVe = CreateProgramElement("varyingVertPerf", programinfo.varyingVertPerf); var fragVe = CreateProgramElement("fragPerf", programinfo.fragPerf); fold.Add(keywordVe); fold.Add(positionVe); fold.Add(varyingVe); fold.Add(fragVe); this.Add(fold); }
public static ShaderProgram Translate( IGalMemory memory, ulong address, ulong addressB, ShaderConfig config) { Operation[] shaderOps = DecodeShader(memory, address, config.Type); if (addressB != 0) { // Dual vertex shader. Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type); shaderOps = Combine(shaderOps, shaderOpsB); } BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps); Dominance.FindDominators(irBlocks[0], irBlocks.Length); Dominance.FindDominanceFrontiers(irBlocks); Ssa.Rename(irBlocks); Optimizer.Optimize(irBlocks); StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks); GlslProgram program = GlslGenerator.Generate(sInfo, config); ShaderProgramInfo spInfo = new ShaderProgramInfo( program.CBufferDescriptors, program.TextureDescriptors); return(new ShaderProgram(spInfo, program.Code)); }
/// <summary> /// Initialize the cache. /// </summary> internal void Initialize() { if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null) { _cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion); bool isReadOnly = _cacheManager.IsReadOnly; HashSet <Hash128> invalidEntries = null; if (isReadOnly) { Logger.Warning?.Print(LogClass.Gpu, "Loading shader cache in read-only mode (cache in use by another program!)"); } else { invalidEntries = new HashSet <Hash128>(); } ReadOnlySpan <Hash128> guestProgramList = _cacheManager.GetGuestProgramList(); using AutoResetEvent progressReportEvent = new AutoResetEvent(false); _shaderCount = 0; _totalShaderCount = guestProgramList.Length; ShaderCacheStateChanged?.Invoke(ShaderCacheState.Start, _shaderCount, _totalShaderCount); Thread progressReportThread = null; if (guestProgramList.Length > 0) { progressReportThread = new Thread(ReportProgress) { Name = "ShaderCache.ProgressReporter", Priority = ThreadPriority.Lowest, IsBackground = true }; progressReportThread.Start(progressReportEvent); } // Make sure these are initialized before doing compilation. Capabilities caps = _context.Capabilities; int maxTaskCount = Math.Min(Environment.ProcessorCount, 8); int programIndex = 0; List <ShaderCompileTask> activeTasks = new List <ShaderCompileTask>(); AutoResetEvent taskDoneEvent = new AutoResetEvent(false); // This thread dispatches tasks to do shader translation, and creates programs that OpenGL will link in the background. // The program link status is checked in a non-blocking manner so that multiple shaders can be compiled at once. while (programIndex < guestProgramList.Length || activeTasks.Count > 0) { if (activeTasks.Count < maxTaskCount && programIndex < guestProgramList.Length) { // Begin a new shader compilation. Hash128 key = guestProgramList[programIndex]; byte[] hostProgramBinary = _cacheManager.GetHostProgramByHash(ref key); bool hasHostCache = hostProgramBinary != null; IProgram hostProgram = null; // If the program sources aren't in the cache, compile from saved guest program. byte[] guestProgram = _cacheManager.GetGuestProgramByHash(ref key); if (guestProgram == null) { Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); // Should not happen, but if someone messed with the cache it's better to catch it. invalidEntries?.Add(key); _shaderCount = ++programIndex; continue; } ReadOnlySpan <byte> guestProgramReadOnlySpan = guestProgram; ReadOnlySpan <GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) { Debug.Assert(cachedShaderEntries.Length == 1); GuestShaderCacheEntry entry = cachedShaderEntries[0]; HostShaderCacheEntry[] hostShaderEntries = null; // Try loading host shader binary. if (hasHostCache) { hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan <byte> hostProgramBinarySpan); hostProgramBinary = hostProgramBinarySpan.ToArray(); hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); activeTasks.Add(task); task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => { ShaderProgram program = null; ShaderProgramInfo shaderProgramInfo = null; if (isHostProgramValid) { // Reconstruct code holder. program = new ShaderProgram(entry.Header.Stage, ""); shaderProgramInfo = hostShaderEntries[0].ToShaderProgramInfo(); ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); return(true); } else { // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. Task compileTask = Task.Run(() => { IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, DefaultFlags | TranslationFlags.Compute); program = Translator.CreateContext(0, gpuAccessor, options).Translate(out shaderProgramInfo); }); task.OnTask(compileTask, (bool _, ShaderCompileTask task) => { ShaderCodeHolder shader = new ShaderCodeHolder(program, shaderProgramInfo, entry.Code); Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); // Compile shader and create program as the shader program binary got invalidated. shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { // As the host program was invalidated, save the new entry in the cache. hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); if (!isReadOnly) { if (hasHostCache) { _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); } else { Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); _cacheManager.AddHostProgram(ref key, hostProgramBinary); } } _cpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shader)); return(true); }); return(false); // Not finished: still need to compile the host program. }); return(false); // Not finished: translating the program. } }); } else { Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); ShaderCodeHolder[] shaders = new ShaderCodeHolder[cachedShaderEntries.Length]; List <ShaderProgram> shaderPrograms = new List <ShaderProgram>(); TransformFeedbackDescriptor[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } TranslationCounts counts = new TranslationCounts(); HostShaderCacheEntry[] hostShaderEntries = null; // Try loading host shader binary. if (hasHostCache) { hostShaderEntries = HostShaderCacheEntry.Parse(hostProgramBinary, out ReadOnlySpan <byte> hostProgramBinarySpan); hostProgramBinary = hostProgramBinarySpan.ToArray(); hostProgram = _context.Renderer.LoadProgramBinary(hostProgramBinary); } ShaderCompileTask task = new ShaderCompileTask(taskDoneEvent); activeTasks.Add(task); GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); task.OnCompiled(hostProgram, (bool isHostProgramValid, ShaderCompileTask task) => { Task compileTask = Task.Run(() => { TranslatorContext[] shaderContexts = null; if (!isHostProgramValid) { shaderContexts = new TranslatorContext[1 + entries.Length]; for (int i = 0; i < entries.Length; i++) { GuestShaderCacheEntry entry = entries[i]; if (entry == null) { continue; } IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); var options = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags); shaderContexts[i + 1] = Translator.CreateContext(0, gpuAccessor, options, counts); if (entry.Header.SizeA != 0) { var options2 = new TranslationOptions(TargetLanguage.Glsl, TargetApi.OpenGL, flags | TranslationFlags.VertexA); shaderContexts[0] = Translator.CreateContext((ulong)entry.Header.Size, gpuAccessor, options2, counts); } } } // Reconstruct code holder. for (int i = 0; i < entries.Length; i++) { GuestShaderCacheEntry entry = entries[i]; if (entry == null) { continue; } ShaderProgram program; ShaderProgramInfo shaderProgramInfo; if (isHostProgramValid) { program = new ShaderProgram(entry.Header.Stage, ""); shaderProgramInfo = hostShaderEntries[i].ToShaderProgramInfo(); } else { IGpuAccessor gpuAccessor = new CachedGpuAccessor(_context, entry.Code, entry.Header.GpuAccessorHeader, entry.TextureDescriptors); int stageIndex = i + 1; TranslatorContext currentStage = shaderContexts[stageIndex]; TranslatorContext nextStage = GetNextStageContext(shaderContexts, stageIndex); TranslatorContext vertexA = stageIndex == 1 ? shaderContexts[0] : null; program = currentStage.Translate(out shaderProgramInfo, nextStage, vertexA); } // NOTE: Vertex B comes first in the shader cache. byte[] code = entry.Code.AsSpan().Slice(0, entry.Header.Size).ToArray(); byte[] code2 = entry.Header.SizeA != 0 ? entry.Code.AsSpan().Slice(entry.Header.Size, entry.Header.SizeA).ToArray() : null; shaders[i] = new ShaderCodeHolder(program, shaderProgramInfo, code, code2); shaderPrograms.Add(program); } }); task.OnTask(compileTask, (bool _, ShaderCompileTask task) => { // If the host program was rejected by the gpu driver or isn't in cache, try to build from program sources again. if (!isHostProgramValid) { Logger.Info?.Print(LogClass.Gpu, $"Host shader {key} got invalidated, rebuilding from guest..."); List <IShader> hostShaders = new List <IShader>(); // Compile shaders and create program as the shader program binary got invalidated. for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); task.OnCompiled(hostProgram, (bool isNewProgramValid, ShaderCompileTask task) => { // As the host program was invalidated, save the new entry in the cache. hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); if (!isReadOnly) { if (hasHostCache) { _cacheManager.ReplaceHostProgram(ref key, hostProgramBinary); } else { Logger.Warning?.Print(LogClass.Gpu, $"Add missing host shader {key} in cache (is the cache incomplete?)"); _cacheManager.AddHostProgram(ref key, hostProgramBinary); } } _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); return(true); }); return(false); // Not finished: still need to compile the host program. } else { _gpProgramsDiskCache.Add(key, new ShaderBundle(hostProgram, shaders)); return(true); } }); return(false); // Not finished: translating the program. }); } _shaderCount = ++programIndex; } // Process the queue. for (int i = 0; i < activeTasks.Count; i++) { ShaderCompileTask task = activeTasks[i]; if (task.IsDone()) { activeTasks.RemoveAt(i--); } } if (activeTasks.Count == maxTaskCount) { // Wait for a task to be done, or for 1ms. // Host shader compilation cannot signal when it is done, // so the 1ms timeout is required to poll status. taskDoneEvent.WaitOne(1); } } if (!isReadOnly) { // Remove entries that are broken in the cache _cacheManager.RemoveManifestEntries(invalidEntries); _cacheManager.FlushToArchive(); _cacheManager.Synchronize(); } progressReportEvent.Set(); progressReportThread?.Join(); ShaderCacheStateChanged?.Invoke(ShaderCacheState.Loaded, _shaderCount, _totalShaderCount); Logger.Info?.Print(LogClass.Gpu, $"Shader cache loaded {_shaderCount} entries."); } }
/// <summary> /// Updates host shaders based on the guest GPU state. /// </summary> private void UpdateShaderState() { ShaderAddresses addresses = new ShaderAddresses(); Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan); ulong baseAddress = _state.State.ShaderBaseAddress.Pack(); for (int index = 0; index < 6; index++) { var shader = _state.State.ShaderState[index]; if (!shader.UnpackEnable() && index != 1) { continue; } addressesArray[index] = baseAddress + shader.Offset; } GpuAccessorState gas = new GpuAccessorState( _state.State.TexturePoolState.Address.Pack(), _state.State.TexturePoolState.MaximumId, (int)_state.State.TextureBufferIndex, _state.State.EarlyZForce, _drawState.Topology); ShaderBundle gs = _channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(ref _state.State, _channel, gas, addresses); byte oldVsClipDistancesWritten = _vsClipDistancesWritten; _drawState.VsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false; _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0; if (oldVsClipDistancesWritten != _vsClipDistancesWritten) { UpdateUserClipState(); } int storageBufferBindingsCount = 0; int uniformBufferBindingsCount = 0; for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Info; _currentProgramInfo[stage] = info; if (info == null) { _channel.TextureManager.SetGraphicsTextures(stage, Array.Empty <TextureBindingInfo>()); _channel.TextureManager.SetGraphicsImages(stage, Array.Empty <TextureBindingInfo>()); _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null); _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null); continue; } var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } _channel.TextureManager.SetGraphicsTextures(stage, textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } _channel.TextureManager.SetGraphicsImages(stage, imageBindings); _channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); _channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); if (info.SBuffers.Count != 0) { storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1); } if (info.CBuffers.Count != 0) { uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1); } } _channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount); _channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount); _context.Renderer.Pipeline.SetProgram(gs.HostProgram); }
/// <summary> /// Performs the compute dispatch operation. /// </summary> /// <param name="argument">Method call argument</param> private void SendSignalingPcasB(int argument) { var memoryManager = _channel.MemoryManager; _3dEngine.FlushUboDirty(); uint qmdAddress = _state.State.SendPcasA; var qmd = _channel.MemoryManager.Read <ComputeQmd>((ulong)qmdAddress << 8); ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB; shaderGpuVa += (uint)qmd.ProgramOffset; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) { continue; } ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); _channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } ulong samplerPoolGpuVa = ((ulong)_state.State.SetTexSamplerPoolAOffsetUpper << 32) | _state.State.SetTexSamplerPoolB; ulong texturePoolGpuVa = ((ulong)_state.State.SetTexHeaderPoolAOffsetUpper << 32) | _state.State.SetTexHeaderPoolB; GpuChannelPoolState poolState = new GpuChannelPoolState( texturePoolGpuVa, _state.State.SetTexHeaderPoolCMaximumIndex, _state.State.SetBindlessTextureConstantBufferSlotSelect); GpuChannelComputeState computeState = new GpuChannelComputeState( qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, sharedMemorySize); CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); _channel.TextureManager.SetComputeSamplerPool(samplerPoolGpuVa, _state.State.SetTexSamplerPoolCMaximumIndex, qmd.SamplerIndex); _channel.TextureManager.SetComputeTexturePool(texturePoolGpuVa, _state.State.SetTexHeaderPoolCMaximumIndex); _channel.TextureManager.SetComputeTextureBufferIndex(_state.State.SetBindlessTextureConstantBufferSlotSelect); ShaderProgramInfo info = cs.Shaders[0].Info; for (int index = 0; index < info.CBuffers.Count; index++) { BufferDescriptor cb = info.CBuffers[index]; // NVN uses the "hardware" constant buffer for anything that is less than 8, // and those are already bound above. // Anything greater than or equal to 8 uses the emulated constant buffers. // They are emulated using global memory loads. if (cb.Slot < 8) { continue; } ulong cbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10; cbDescAddress += (ulong)cbDescOffset; SbDescriptor cbDescriptor = _channel.MemoryManager.Physical.Read <SbDescriptor>(cbDescAddress); _channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read <SbDescriptor>(sbDescAddress); _channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); } _channel.BufferManager.SetComputeStorageBufferBindings(info.SBuffers); _channel.BufferManager.SetComputeUniformBufferBindings(info.CBuffers); int maxTextureBinding = -1; int maxImageBinding = -1; TextureBindingInfo[] textureBindings = _channel.TextureManager.RentComputeTextureBindings(info.Textures.Count); for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); if (descriptor.Binding > maxTextureBinding) { maxTextureBinding = descriptor.Binding; } } TextureBindingInfo[] imageBindings = _channel.TextureManager.RentComputeImageBindings(info.Images.Count); for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); if (descriptor.Binding > maxImageBinding) { maxImageBinding = descriptor.Binding; } } _channel.TextureManager.SetComputeMaxBindings(maxTextureBinding, maxImageBinding); // Should never return false for mismatching spec state, since the shader was fetched above. _channel.TextureManager.CommitComputeBindings(cs.SpecializationState); _channel.BufferManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute(qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); _3dEngine.ForceShaderUpdate(); }
/// <summary> /// Updates host shaders based on the guest GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateShaderState(GpuState state) { ShaderAddresses addresses = new ShaderAddresses(); Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan); ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack(); for (int index = 0; index < 6; index++) { var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index); if (!shader.UnpackEnable() && index != 1) { continue; } addressesArray[index] = baseAddress + shader.Offset; } ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses); _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false; int storageBufferBindingsCount = 0; int uniformBufferBindingsCount = 0; for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Info; _currentProgramInfo[stage] = info; if (info == null) { TextureManager.SetGraphicsTextures(stage, Array.Empty <TextureBindingInfo>()); TextureManager.SetGraphicsImages(stage, Array.Empty <TextureBindingInfo>()); BufferManager.SetGraphicsStorageBufferBindings(stage, null); BufferManager.SetGraphicsUniformBufferBindings(stage, null); continue; } var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetGraphicsTextures(stage, textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetGraphicsImages(stage, imageBindings); BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); if (info.SBuffers.Count != 0) { storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1); } if (info.CBuffers.Count != 0) { uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1); } } BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount); BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount); _context.Renderer.Pipeline.SetProgram(gs.HostProgram); }
/// <summary> /// Dispatches compute work. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> public void Dispatch(GpuState state, int argument) { uint qmdAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress); var qmd = _context.MemoryAccessor.Read <ComputeQmd>((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress); ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)qmd.ProgramOffset; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); Shader.Shader cs = ShaderCache.GetComputeShader( shaderGpuVa, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, sharedMemorySize); CurrentCpMeta = cs.Meta; _context.Renderer.Pipeline.SetProgram(cs.HostProgram); var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState); TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState); TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex)); ShaderProgramInfo info = cs.Meta.Info[0]; uint sbEnableMask = 0; uint ubEnableMask = 0; for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) { continue; } ubEnableMask |= 1u << index; ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } for (int index = 0; index < info.CBuffers.Count; index++) { BufferDescriptor cb = info.CBuffers[index]; // NVN uses the "hardware" constant buffer for anything that is less than 8, // and those are already bound above. // Anything greater than or equal to 8 uses the emulated constant buffers. // They are emulated using global memory loads. if (cb.Slot < 8) { continue; } ubEnableMask |= 1u << cb.Slot; ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int cbDescOffset = 0x260 + cb.Slot * 0x10; cbDescAddress += (ulong)cbDescOffset; ReadOnlySpan <byte> cbDescriptorData = _context.PhysicalMemory.GetSpan(cbDescAddress, 0x10); SbDescriptor cbDescriptor = MemoryMarshal.Cast <byte, SbDescriptor>(cbDescriptorData)[0]; BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; sbEnableMask |= 1u << sb.Slot; ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; ReadOnlySpan <byte> sbDescriptorData = _context.PhysicalMemory.GetSpan(sbDescAddress, 0x10); SbDescriptor sbDescriptor = MemoryMarshal.Cast <byte, SbDescriptor>(sbDescriptorData)[0]; BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); } ubEnableMask = 0; for (int index = 0; index < info.CBuffers.Count; index++) { ubEnableMask |= 1u << info.CBuffers[index].Slot; } BufferManager.SetComputeStorageBufferEnableMask(sbEnableMask); BufferManager.SetComputeUniformBufferEnableMask(ubEnableMask); var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = GetTarget(descriptor.Type); if (descriptor.IsBindless) { textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot); } else { textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } } TextureManager.SetComputeTextures(textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = GetTarget(descriptor.Type); imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } TextureManager.SetComputeImages(imageBindings); BufferManager.CommitComputeBindings(); TextureManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute( qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); UpdateShaderState(state); }
/// <summary> /// Dispatches compute work. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> public void Dispatch(GpuState state, int argument) { FlushUboDirty(); uint qmdAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress); var qmd = _context.MemoryManager.Read <ComputeQmd>((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress); ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)qmd.ProgramOffset; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) { continue; } ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } ShaderBundle cs = ShaderCache.GetComputeShader( state, shaderGpuVa, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, sharedMemorySize); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState); var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState); TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex)); ShaderProgramInfo info = cs.Shaders[0].Info; for (int index = 0; index < info.CBuffers.Count; index++) { BufferDescriptor cb = info.CBuffers[index]; // NVN uses the "hardware" constant buffer for anything that is less than 8, // and those are already bound above. // Anything greater than or equal to 8 uses the emulated constant buffers. // They are emulated using global memory loads. if (cb.Slot < 8) { continue; } ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10; cbDescAddress += (ulong)cbDescOffset; SbDescriptor cbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(cbDescAddress); BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; SbDescriptor sbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(sbDescAddress); BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); } BufferManager.SetComputeStorageBufferBindings(info.SBuffers); BufferManager.SetComputeUniformBufferBindings(info.CBuffers); var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetComputeTextures(textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetComputeImages(imageBindings); TextureManager.CommitComputeBindings(); BufferManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute( qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); _forceShaderUpdate = true; }
internal ShaderProgram(ShaderProgramInfo info, string code) { Info = info; Code = code; }
/// <summary> /// Loads all shaders from the cache. /// </summary> /// <param name="context">GPU context</param> /// <param name="loader">Parallel disk cache loader</param> public void LoadShaders(GpuContext context, ParallelDiskCacheLoader loader) { if (!CacheExists()) { return; } Stream hostTocFileStream = null; Stream hostDataFileStream = null; try { using var tocFileStream = DiskCacheCommon.OpenFile(_basePath, SharedTocFileName, writable: false); using var dataFileStream = DiskCacheCommon.OpenFile(_basePath, SharedDataFileName, writable: false); using var guestTocFileStream = _guestStorage.OpenTocFileStream(); using var guestDataFileStream = _guestStorage.OpenDataFileStream(); BinarySerializer tocReader = new BinarySerializer(tocFileStream); BinarySerializer dataReader = new BinarySerializer(dataFileStream); TocHeader header = new TocHeader(); if (!tocReader.TryRead(ref header) || header.Magic != TocsMagic) { throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); } if (header.FormatVersion != FileFormatVersionPacked) { throw new DiskCacheLoadException(DiskCacheLoadResult.IncompatibleVersion); } bool loadHostCache = header.CodeGenVersion == CodeGenVersion; int programIndex = 0; DataEntry entry = new DataEntry(); while (tocFileStream.Position < tocFileStream.Length && loader.Active) { ulong dataOffset = 0; tocReader.Read(ref dataOffset); if ((ulong)dataOffset >= (ulong)dataFileStream.Length) { throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); } dataFileStream.Seek((long)dataOffset, SeekOrigin.Begin); dataReader.BeginCompression(); dataReader.Read(ref entry); uint stagesBitMask = entry.StagesBitMask; if ((stagesBitMask & ~0x3fu) != 0) { throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); } bool isCompute = stagesBitMask == 0; if (isCompute) { stagesBitMask = 1; } CachedShaderStage[] shaders = new CachedShaderStage[isCompute ? 1 : Constants.ShaderStages + 1]; DataEntryPerStage stageEntry = new DataEntryPerStage(); while (stagesBitMask != 0) { int stageIndex = BitOperations.TrailingZeroCount(stagesBitMask); dataReader.Read(ref stageEntry); ShaderProgramInfo info = stageIndex != 0 || isCompute?ReadShaderProgramInfo(ref dataReader) : null; (byte[] guestCode, byte[] cb1Data) = _guestStorage.LoadShader( guestTocFileStream, guestDataFileStream, stageEntry.GuestCodeIndex); shaders[stageIndex] = new CachedShaderStage(info, guestCode, cb1Data); stagesBitMask &= ~(1u << stageIndex); } ShaderSpecializationState specState = ShaderSpecializationState.Read(ref dataReader); dataReader.EndCompression(); if (loadHostCache) { byte[] hostCode = ReadHostCode(context, ref hostTocFileStream, ref hostDataFileStream, programIndex); if (hostCode != null) { bool hasFragmentShader = shaders.Length > 5 && shaders[5] != null; int fragmentOutputMap = hasFragmentShader ? shaders[5].Info.FragmentOutputMap : -1; IProgram hostProgram = context.Renderer.LoadProgramBinary(hostCode, hasFragmentShader, new ShaderInfo(fragmentOutputMap)); CachedShaderProgram program = new CachedShaderProgram(hostProgram, specState, shaders); loader.QueueHostProgram(program, hostProgram, programIndex, isCompute); } else { loadHostCache = false; } } if (!loadHostCache) { loader.QueueGuestProgram(shaders, specState, programIndex, isCompute); } loader.CheckCompilation(); programIndex++; } } finally { _guestStorage.ClearMemoryCache(); hostTocFileStream?.Dispose(); hostDataFileStream?.Dispose(); } }
/// <summary> /// Reads the host code for a given shader, if existent. /// </summary> /// <param name="context">GPU context</param> /// <param name="tocFileStream">Host TOC file stream, intialized if needed</param> /// <param name="dataFileStream">Host data file stream, initialized if needed</param> /// <param name="guestShaders">Guest shader code for each active stage</param> /// <param name="programIndex">Index of the program on the cache</param> /// <param name="expectedTimestamp">Timestamp of the shared cache file. The host file must be newer than it</param> /// <returns>Host binary code, or null if not found</returns> private (byte[], CachedShaderStage[]) ReadHostCode( GpuContext context, ref Stream tocFileStream, ref Stream dataFileStream, GuestCodeAndCbData?[] guestShaders, int programIndex, ulong expectedTimestamp) { if (tocFileStream == null && dataFileStream == null) { string tocFilePath = Path.Combine(_basePath, GetHostTocFileName(context)); string dataFilePath = Path.Combine(_basePath, GetHostDataFileName(context)); if (!File.Exists(tocFilePath) || !File.Exists(dataFilePath)) { return(null, null); } tocFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostTocFileName(context), writable: false); dataFileStream = DiskCacheCommon.OpenFile(_basePath, GetHostDataFileName(context), writable: false); BinarySerializer tempTocReader = new BinarySerializer(tocFileStream); TocHeader header = new TocHeader(); tempTocReader.Read(ref header); if (header.Timestamp < expectedTimestamp) { return(null, null); } } int offset = Unsafe.SizeOf <TocHeader>() + programIndex * Unsafe.SizeOf <OffsetAndSize>(); if (offset + Unsafe.SizeOf <OffsetAndSize>() > tocFileStream.Length) { return(null, null); } if ((ulong)offset >= (ulong)dataFileStream.Length) { throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); } tocFileStream.Seek(offset, SeekOrigin.Begin); BinarySerializer tocReader = new BinarySerializer(tocFileStream); OffsetAndSize offsetAndSize = new OffsetAndSize(); tocReader.Read(ref offsetAndSize); if (offsetAndSize.Offset >= (ulong)dataFileStream.Length) { throw new DiskCacheLoadException(DiskCacheLoadResult.FileCorruptedGeneric); } dataFileStream.Seek((long)offsetAndSize.Offset, SeekOrigin.Begin); byte[] hostCode = new byte[offsetAndSize.UncompressedSize]; BinarySerializer.ReadCompressed(dataFileStream, hostCode); CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length]; BinarySerializer dataReader = new BinarySerializer(dataFileStream); dataFileStream.Seek((long)(offsetAndSize.Offset + offsetAndSize.CompressedSize), SeekOrigin.Begin); dataReader.BeginCompression(); for (int index = 0; index < guestShaders.Length; index++) { if (!guestShaders[index].HasValue) { continue; } GuestCodeAndCbData guestShader = guestShaders[index].Value; ShaderProgramInfo info = index != 0 || guestShaders.Length == 1 ? ReadShaderProgramInfo(ref dataReader) : null; shaders[index] = new CachedShaderStage(info, guestShader.Code, guestShader.Cb1Data); } dataReader.EndCompression(); return(hostCode, shaders); }
/// <summary> /// Creates a new instance of the shader code holder. /// </summary> /// <param name="info">Shader program information</param> /// <param name="code">Maxwell binary shader code</param> /// <param name="cb1Data">Constant buffer 1 data accessed by the shader</param> public CachedShaderStage(ShaderProgramInfo info, byte[] code, byte[] cb1Data) { Info = info; Code = code; Cb1Data = cb1Data; }
internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config, out ShaderProgramInfo shaderProgramInfo) { var cfgs = new ControlFlowGraph[functions.Length]; var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; for (int i = 0; i < functions.Length; i++) { cfgs[i] = ControlFlowGraph.Create(functions[i].Code); if (i != 0) { frus[i] = RegisterUsage.RunPass(cfgs[i]); } } Function[] funcs = new Function[functions.Length]; for (int i = 0; i < functions.Length; i++) { var cfg = cfgs[i]; int inArgumentsCount = 0; int outArgumentsCount = 0; if (i != 0) { var fru = frus[i]; inArgumentsCount = fru.InArguments.Length; outArgumentsCount = fru.OutArguments.Length; } if (cfg.Blocks.Length != 0) { RegisterUsage.FixupCalls(cfg.Blocks, frus); Dominance.FindDominators(cfg); Dominance.FindDominanceFrontiers(cfg.Blocks); Ssa.Rename(cfg.Blocks); Optimizer.RunPass(cfg.Blocks, config); Rewriter.RunPass(cfg.Blocks, config); } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); } StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); ShaderProgram program; switch (config.Options.TargetLanguage) { case TargetLanguage.Glsl: program = new ShaderProgram(config.Stage, GlslGenerator.Generate(sInfo, config)); break; default: throw new NotImplementedException(config.Options.TargetLanguage.ToString()); } shaderProgramInfo = new ShaderProgramInfo( config.GetConstantBufferDescriptors(), config.GetStorageBufferDescriptors(), config.GetTextureDescriptors(), config.GetImageDescriptors(), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.UsedFeatures.HasFlag(FeatureFlags.RtLayer), config.ClipDistancesWritten, config.OmapTargets); return(program); }
/// <summary> /// Updates host shaders based on the guest GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateShaderState(GpuState state) { ShaderAddresses addresses = new ShaderAddresses(); Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan); ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack(); for (int index = 0; index < 6; index++) { var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index); if (!shader.UnpackEnable() && index != 1) { continue; } addressesArray[index] = baseAddress + shader.Offset; } ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses); _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false; for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info; _currentProgramInfo[stage] = info; if (info == null) { continue; } var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = GetTarget(descriptor.Type); if (descriptor.IsBindless) { textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset, descriptor.Flags); } else { textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags); } } TextureManager.SetGraphicsTextures(stage, textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = GetTarget(descriptor.Type); imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetGraphicsImages(stage, imageBindings); uint sbEnableMask = 0; uint ubEnableMask = 0; for (int index = 0; index < info.SBuffers.Count; index++) { sbEnableMask |= 1u << info.SBuffers[index].Slot; } for (int index = 0; index < info.CBuffers.Count; index++) { ubEnableMask |= 1u << info.CBuffers[index].Slot; } BufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask); BufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask); } _context.Renderer.Pipeline.SetProgram(gs.HostProgram); }
/// <summary> /// Migrates from the old cache format to the new one. /// </summary> /// <param name="context">GPU context</param> /// <param name="hostStorage">Disk cache host storage (used to create the new shader files)</param> /// <returns>Number of migrated shaders</returns> public static int MigrateFromLegacyCache(GpuContext context, DiskCacheHostStorage hostStorage) { string baseCacheDirectory = CacheHelper.GetBaseCacheDirectory(GraphicsConfig.TitleId); string cacheDirectory = CacheHelper.GenerateCachePath(baseCacheDirectory, CacheGraphicsApi.Guest, "", "program"); // If the directory does not exist, we have no old cache. // Exist early as the CacheManager constructor will create the directories. if (!Directory.Exists(cacheDirectory)) { return(0); } if (GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null) { CacheManager cacheManager = new CacheManager(CacheGraphicsApi.OpenGL, CacheHashType.XxHash128, "glsl", GraphicsConfig.TitleId, ShaderCodeGenVersion); bool isReadOnly = cacheManager.IsReadOnly; HashSet <Hash128> invalidEntries = null; if (isReadOnly) { Logger.Warning?.Print(LogClass.Gpu, "Loading shader cache in read-only mode (cache in use by another program!)"); } else { invalidEntries = new HashSet <Hash128>(); } ReadOnlySpan <Hash128> guestProgramList = cacheManager.GetGuestProgramList(); for (int programIndex = 0; programIndex < guestProgramList.Length; programIndex++) { Hash128 key = guestProgramList[programIndex]; byte[] guestProgram = cacheManager.GetGuestProgramByHash(ref key); if (guestProgram == null) { Logger.Error?.Print(LogClass.Gpu, $"Ignoring orphan shader hash {key} in cache (is the cache incomplete?)"); continue; } ReadOnlySpan <byte> guestProgramReadOnlySpan = guestProgram; ReadOnlySpan <GuestShaderCacheEntry> cachedShaderEntries = GuestShaderCacheEntry.Parse(ref guestProgramReadOnlySpan, out GuestShaderCacheHeader fileHeader); if (cachedShaderEntries[0].Header.Stage == ShaderStage.Compute) { Debug.Assert(cachedShaderEntries.Length == 1); GuestShaderCacheEntry entry = cachedShaderEntries[0]; byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); Span <byte> codeSpan = entry.Code; byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray(); ShaderProgramInfo info = new ShaderProgramInfo( Array.Empty <BufferDescriptor>(), Array.Empty <BufferDescriptor>(), Array.Empty <TextureDescriptor>(), Array.Empty <TextureDescriptor>(), ShaderStage.Compute, false, false, 0, 0); GpuChannelComputeState computeState = new GpuChannelComputeState( entry.Header.GpuAccessorHeader.ComputeLocalSizeX, entry.Header.GpuAccessorHeader.ComputeLocalSizeY, entry.Header.GpuAccessorHeader.ComputeLocalSizeZ, entry.Header.GpuAccessorHeader.ComputeLocalMemorySize, entry.Header.GpuAccessorHeader.ComputeSharedMemorySize); ShaderSpecializationState specState = new ShaderSpecializationState(computeState); foreach (var td in entry.TextureDescriptors) { var handle = td.Key; var data = td.Value; specState.RegisterTexture( 0, handle, -1, data.UnpackFormat(), data.UnpackSrgb(), data.UnpackTextureTarget(), data.UnpackTextureCoordNormalized()); } CachedShaderStage shader = new CachedShaderStage(info, code, cb1Data); CachedShaderProgram program = new CachedShaderProgram(null, specState, shader); hostStorage.AddShader(context, program, ReadOnlySpan <byte> .Empty); } else { Debug.Assert(cachedShaderEntries.Length == Constants.ShaderStages); CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1]; List <ShaderProgram> shaderPrograms = new List <ShaderProgram>(); TransformFeedbackDescriptorOld[] tfd = CacheHelper.ReadTransformFeedbackInformation(ref guestProgramReadOnlySpan, fileHeader); GuestShaderCacheEntry[] entries = cachedShaderEntries.ToArray(); GuestGpuAccessorHeader accessorHeader = entries[0].Header.GpuAccessorHeader; TessMode tessMode = new TessMode(); int tessPatchType = accessorHeader.TessellationModePacked & 3; int tessSpacing = (accessorHeader.TessellationModePacked >> 2) & 3; bool tessCw = (accessorHeader.TessellationModePacked & 0x10) != 0; tessMode.Packed = (uint)tessPatchType; tessMode.Packed |= (uint)(tessSpacing << 4); if (tessCw) { tessMode.Packed |= 0x100; } PrimitiveTopology topology = accessorHeader.PrimitiveTopology switch { InputTopology.Lines => PrimitiveTopology.Lines, InputTopology.LinesAdjacency => PrimitiveTopology.LinesAdjacency, InputTopology.Triangles => PrimitiveTopology.Triangles, InputTopology.TrianglesAdjacency => PrimitiveTopology.TrianglesAdjacency, _ => PrimitiveTopology.Points }; GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState( accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce), topology, tessMode); TransformFeedbackDescriptor[] tfdNew = null; if (tfd != null) { tfdNew = new TransformFeedbackDescriptor[tfd.Length]; for (int tfIndex = 0; tfIndex < tfd.Length; tfIndex++) { Array32 <uint> varyingLocations = new Array32 <uint>(); Span <byte> varyingLocationsSpan = MemoryMarshal.Cast <uint, byte>(varyingLocations.ToSpan()); tfd[tfIndex].VaryingLocations.CopyTo(varyingLocationsSpan.Slice(0, tfd[tfIndex].VaryingLocations.Length)); tfdNew[tfIndex] = new TransformFeedbackDescriptor( tfd[tfIndex].BufferIndex, tfd[tfIndex].Stride, tfd[tfIndex].VaryingLocations.Length, ref varyingLocations); } } ShaderSpecializationState specState = new ShaderSpecializationState(graphicsState, tfdNew); for (int i = 0; i < entries.Length; i++) { GuestShaderCacheEntry entry = entries[i]; if (entry == null) { continue; } ShaderProgramInfo info = new ShaderProgramInfo( Array.Empty <BufferDescriptor>(), Array.Empty <BufferDescriptor>(), Array.Empty <TextureDescriptor>(), Array.Empty <TextureDescriptor>(), (ShaderStage)(i + 1), false, false, 0, 0); // NOTE: Vertex B comes first in the shader cache. byte[] code = entry.Code.AsSpan(0, entry.Header.Size - entry.Header.Cb1DataSize).ToArray(); byte[] code2 = entry.Header.SizeA != 0 ? entry.Code.AsSpan(entry.Header.Size, entry.Header.SizeA).ToArray() : null; Span <byte> codeSpan = entry.Code; byte[] cb1Data = codeSpan.Slice(codeSpan.Length - entry.Header.Cb1DataSize).ToArray(); shaders[i + 1] = new CachedShaderStage(info, code, cb1Data); if (code2 != null) { shaders[0] = new CachedShaderStage(null, code2, cb1Data); } foreach (var td in entry.TextureDescriptors) { var handle = td.Key; var data = td.Value; specState.RegisterTexture( i, handle, -1, data.UnpackFormat(), data.UnpackSrgb(), data.UnpackTextureTarget(), data.UnpackTextureCoordNormalized()); } } CachedShaderProgram program = new CachedShaderProgram(null, specState, shaders); hostStorage.AddShader(context, program, ReadOnlySpan <byte> .Empty); } } return(guestProgramList.Length); } return(0); } }
internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config, out ShaderProgramInfo shaderProgramInfo, int sizeA = 0) { var cfgs = new ControlFlowGraph[functions.Length]; var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length]; for (int i = 0; i < functions.Length; i++) { cfgs[i] = ControlFlowGraph.Create(functions[i].Code); if (i != 0) { frus[i] = RegisterUsage.RunPass(cfgs[i]); } } Function[] funcs = new Function[functions.Length]; for (int i = 0; i < functions.Length; i++) { var cfg = cfgs[i]; int inArgumentsCount = 0; int outArgumentsCount = 0; if (i != 0) { var fru = frus[i]; inArgumentsCount = fru.InArguments.Length; outArgumentsCount = fru.OutArguments.Length; } if (cfg.Blocks.Length != 0) { RegisterUsage.FixupCalls(cfg.Blocks, frus); Dominance.FindDominators(cfg); Dominance.FindDominanceFrontiers(cfg.Blocks); Ssa.Rename(cfg.Blocks); Optimizer.RunPass(cfg.Blocks, config); Rewriter.RunPass(cfg.Blocks, config); } funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); } StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); GlslProgram program = GlslGenerator.Generate(sInfo, config); shaderProgramInfo = new ShaderProgramInfo( program.CBufferDescriptors, program.SBufferDescriptors, program.TextureDescriptors, program.ImageDescriptors, sInfo.UsesInstanceId); string glslCode = program.Code; return(new ShaderProgram(config.Stage, glslCode, config.Size, sizeA)); }
/// <summary> /// Creates a new instace of the shader meta data. /// </summary> /// <param name="hostProgram">Host shader program</param> /// <param name="infos">Shader program information, per stage</param> public ShaderMeta(IProgram hostProgram, params ShaderProgramInfo[] infos) { _ubBindingPoints = new int[UbsPerStage * ShaderStages]; _sbBindingPoints = new int[SbsPerStage * ShaderStages]; _textureUnits = new int[TexsPerStage * ShaderStages]; _imageUnits = new int[ImgsPerStage * ShaderStages]; for (int index = 0; index < _ubBindingPoints.Length; index++) { _ubBindingPoints[index] = -1; } for (int index = 0; index < _sbBindingPoints.Length; index++) { _sbBindingPoints[index] = -1; } for (int index = 0; index < _textureUnits.Length; index++) { _textureUnits[index] = -1; } for (int index = 0; index < _imageUnits.Length; index++) { _imageUnits[index] = -1; } int ubBindingPoint = 0; int sbBindingPoint = 0; int textureUnit = 0; int imageUnit = 0; Info = new ShaderProgramInfo[infos.Length]; for (int index = 0; index < infos.Length; index++) { ShaderProgramInfo info = infos[index]; if (info == null) { continue; } foreach (BufferDescriptor descriptor in info.CBuffers) { hostProgram.SetUniformBufferBindingPoint(descriptor.Name, ubBindingPoint); int bpIndex = (int)info.Stage << UbStageShift | descriptor.Slot; _ubBindingPoints[bpIndex] = ubBindingPoint; ubBindingPoint++; } foreach (BufferDescriptor descriptor in info.SBuffers) { hostProgram.SetStorageBufferBindingPoint(descriptor.Name, sbBindingPoint); int bpIndex = (int)info.Stage << SbStageShift | descriptor.Slot; _sbBindingPoints[bpIndex] = sbBindingPoint; sbBindingPoint++; } int samplerIndex = 0; foreach (TextureDescriptor descriptor in info.Textures) { hostProgram.SetImageUnit(descriptor.Name, textureUnit); int uIndex = (int)info.Stage << TexStageShift | samplerIndex++; _textureUnits[uIndex] = textureUnit; textureUnit++; } int imageIndex = 0; foreach (TextureDescriptor descriptor in info.Images) { hostProgram.SetImageUnit(descriptor.Name, imageUnit); int uIndex = (int)info.Stage << ImgStageShift | imageIndex++; _imageUnits[uIndex] = imageUnit; imageUnit++; } Info[index] = info; } }