Example #1
0
        /// <summary>
        /// Write the guest GpuAccessor informations to the given stream.
        /// </summary>
        /// <param name="stream">The stream to write the guest GpuAcessor</param>
        /// <param name="shaderContext">The shader tranlator context in use</param>
        /// <returns>The guest gpu accessor header</returns>
        private static GuestGpuAccessorHeader WriteGuestGpuAccessorCache(Stream stream, TranslatorContext shaderContext)
        {
            BinaryWriter writer = new BinaryWriter(stream);

            GuestGpuAccessorHeader header = CreateGuestGpuAccessorCache(shaderContext.GpuAccessor);

            // If we have a full gpu accessor, cache textures descriptors
            if (shaderContext.GpuAccessor is GpuAccessor gpuAccessor)
            {
                HashSet <int> textureHandlesInUse = shaderContext.TextureHandlesForCache;

                header.TextureDescriptorCount = textureHandlesInUse.Count;

                foreach (int textureHandle in textureHandlesInUse)
                {
                    GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle)).ToCache();

                    textureDescriptor.Handle = (uint)textureHandle;

                    writer.WriteStruct(textureDescriptor);
                }
            }

            return(header);
        }
Example #2
0
        /// <summary>
        /// Create guest shader cache entries from the runtime contexts.
        /// </summary>
        /// <param name="memoryManager">The GPU memory manager in use</param>
        /// <param name="shaderContexts">The runtime contexts</param>
        /// <returns>Guest shader cahe entries from the runtime contexts</returns>
        public static GuestShaderCacheEntry[] CreateShaderCacheEntries(MemoryManager memoryManager, ReadOnlySpan <TranslatorContext> shaderContexts)
        {
            int startIndex = shaderContexts.Length > 1 ? 1 : 0;

            GuestShaderCacheEntry[] entries = new GuestShaderCacheEntry[shaderContexts.Length - startIndex];

            for (int i = startIndex; i < shaderContexts.Length; i++)
            {
                TranslatorContext context = shaderContexts[i];

                if (context == null)
                {
                    continue;
                }

                TranslatorContext translatorContext2 = i == 1 ? shaderContexts[0] : null;

                int sizeA = translatorContext2 != null ? translatorContext2.Size : 0;

                byte[] code = new byte[context.Size + sizeA];

                memoryManager.GetSpan(context.Address, context.Size).CopyTo(code);

                if (translatorContext2 != null)
                {
                    memoryManager.GetSpan(translatorContext2.Address, sizeA).CopyTo(code.AsSpan().Slice(context.Size, sizeA));
                }

                GuestGpuAccessorHeader gpuAccessorHeader = CreateGuestGpuAccessorCache(context.GpuAccessor);

                if (context.GpuAccessor is GpuAccessor)
                {
                    gpuAccessorHeader.TextureDescriptorCount = context.TextureHandlesForCache.Count;
                }

                GuestShaderCacheEntryHeader header = new GuestShaderCacheEntryHeader(context.Stage, context.Size, sizeA, gpuAccessorHeader);

                GuestShaderCacheEntry entry = new GuestShaderCacheEntry(header, code);

                if (context.GpuAccessor is GpuAccessor gpuAccessor)
                {
                    foreach (int textureHandle in context.TextureHandlesForCache)
                    {
                        GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle, -1)).ToCache();

                        textureDescriptor.Handle = (uint)textureHandle;

                        entry.TextureDescriptors.Add(textureHandle, textureDescriptor);
                    }
                }

                entries[i - startIndex] = entry;
            }

            return(entries);
        }
Example #3
0
        /// <summary>
        /// Creates a new instance of the cached GPU state accessor for shader translation.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="data">The data of the shader</param>
        /// <param name="header">The cache of the GPU accessor</param>
        /// <param name="guestTextureDescriptors">The cache of the texture descriptors</param>
        public CachedGpuAccessor(GpuContext context, ReadOnlyMemory <byte> data, GuestGpuAccessorHeader header, Dictionary <int, GuestTextureDescriptor> guestTextureDescriptors)
        {
            _context            = context;
            _data               = data;
            _header             = header;
            _textureDescriptors = new Dictionary <int, GuestTextureDescriptor>();

            foreach (KeyValuePair <int, GuestTextureDescriptor> guestTextureDescriptor in guestTextureDescriptors)
            {
                _textureDescriptors.Add(guestTextureDescriptor.Key, guestTextureDescriptor.Value);
            }
        }
Example #4
0
        /// <summary>
        /// Get the shader program information for use on the shader cache.
        /// </summary>
        /// <param name="tfd">The current transform feedback descriptors used</param>
        /// <param name="shaderContexts">The shader translators context in use</param>
        /// <param name="programCode">The resulting raw shader program code</param>
        /// <param name="programCodeHash">The resulting raw shader program code hash</param>
        /// <param name="entries">The resulting guest shader entries header</param>
        private void GetProgramInformations(TransformFeedbackDescriptor[] tfd, ReadOnlySpan <TranslatorContext> shaderContexts, out byte[] programCode, out Hash128 programCodeHash, out GuestShaderCacheEntryHeader[] entries)
        {
            GuestShaderCacheEntryHeader ComputeStage(Stream stream, TranslatorContext context)
            {
                if (context == null)
                {
                    return(new GuestShaderCacheEntryHeader());
                }

                ReadOnlySpan <byte> data = _context.MemoryManager.GetSpan(context.Address, context.Size);

                stream.Write(data);

                int size  = data.Length;
                int sizeA = 0;

                if (context.AddressA != 0)
                {
                    data = _context.MemoryManager.GetSpan(context.AddressA, context.SizeA);

                    sizeA = data.Length;

                    stream.Write(data);
                }

                GuestGpuAccessorHeader gpuAccessorHeader = WriteGuestGpuAccessorCache(stream, context);

                return(new GuestShaderCacheEntryHeader(context.Stage, size, sizeA, gpuAccessorHeader));
            }

            entries = new GuestShaderCacheEntryHeader[shaderContexts.Length];

            using (MemoryStream stream = new MemoryStream())
            {
                for (int i = 0; i < shaderContexts.Length; i++)
                {
                    entries[i] = ComputeStage(stream, shaderContexts[i]);
                }

                WriteTransformationFeedbackInformation(stream, tfd);

                programCode     = stream.ToArray();
                programCodeHash = _cacheManager.ComputeHash(programCode);
            }
        }
Example #5
0
        /// <summary>
        /// Creates a new instance of the cached GPU state accessor for shader translation.
        /// </summary>
        /// <param name="context">GPU context</param>
        /// <param name="data">The data of the shader</param>
        /// <param name="cb1Data">The constant buffer 1 data of the shader</param>
        /// <param name="header">The cache of the GPU accessor</param>
        /// <param name="guestTextureDescriptors">The cache of the texture descriptors</param>
        public CachedGpuAccessor(
            GpuContext context,
            ReadOnlyMemory <byte> data,
            ReadOnlyMemory <byte> cb1Data,
            GuestGpuAccessorHeader header,
            IReadOnlyDictionary <int, GuestTextureDescriptor> guestTextureDescriptors,
            TransformFeedbackDescriptor[] tfd) : base(context)
        {
            _data               = data;
            _cb1Data            = cb1Data;
            _header             = header;
            _textureDescriptors = new Dictionary <int, GuestTextureDescriptor>();

            foreach (KeyValuePair <int, GuestTextureDescriptor> guestTextureDescriptor in guestTextureDescriptors)
            {
                _textureDescriptors.Add(guestTextureDescriptor.Key, guestTextureDescriptor.Value);
            }

            _tfd = tfd;
        }
Example #6
0
        /// <summary>
        /// Create guest shader cache entries from the runtime contexts.
        /// </summary>
        /// <param name="channel">The GPU channel in use</param>
        /// <param name="shaderContexts">The runtime contexts</param>
        /// <returns>Guest shader cahe entries from the runtime contexts</returns>
        public static GuestShaderCacheEntry[] CreateShaderCacheEntries(GpuChannel channel, ReadOnlySpan <TranslatorContext> shaderContexts)
        {
            MemoryManager memoryManager = channel.MemoryManager;

            int startIndex = shaderContexts.Length > 1 ? 1 : 0;

            GuestShaderCacheEntry[] entries = new GuestShaderCacheEntry[shaderContexts.Length - startIndex];

            for (int i = startIndex; i < shaderContexts.Length; i++)
            {
                TranslatorContext context = shaderContexts[i];

                if (context == null)
                {
                    continue;
                }

                GpuAccessor gpuAccessor = context.GpuAccessor as GpuAccessor;

                ulong cb1DataAddress;
                int   cb1DataSize = gpuAccessor?.Cb1DataSize ?? 0;

                if (context.Stage == ShaderStage.Compute)
                {
                    cb1DataAddress = channel.BufferManager.GetComputeUniformBufferAddress(1);
                }
                else
                {
                    int stageIndex = context.Stage switch
                    {
                        ShaderStage.TessellationControl => 1,
                        ShaderStage.TessellationEvaluation => 2,
                        ShaderStage.Geometry => 3,
                        ShaderStage.Fragment => 4,
                        _ => 0
                    };

                    cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, 1);
                }

                int size = context.Size;

                TranslatorContext translatorContext2 = i == 1 ? shaderContexts[0] : null;

                int sizeA = translatorContext2 != null ? translatorContext2.Size : 0;

                byte[] code = new byte[size + cb1DataSize + sizeA];

                memoryManager.GetSpan(context.Address, size).CopyTo(code);

                if (cb1DataAddress != 0 && cb1DataSize != 0)
                {
                    memoryManager.Physical.GetSpan(cb1DataAddress, cb1DataSize).CopyTo(code.AsSpan(size, cb1DataSize));
                }

                if (translatorContext2 != null)
                {
                    memoryManager.GetSpan(translatorContext2.Address, sizeA).CopyTo(code.AsSpan(size + cb1DataSize, sizeA));
                }

                GuestGpuAccessorHeader gpuAccessorHeader = CreateGuestGpuAccessorCache(context.GpuAccessor);

                if (gpuAccessor != null)
                {
                    gpuAccessorHeader.TextureDescriptorCount = context.TextureHandlesForCache.Count;
                }

                GuestShaderCacheEntryHeader header = new GuestShaderCacheEntryHeader(
                    context.Stage,
                    size + cb1DataSize,
                    sizeA,
                    cb1DataSize,
                    gpuAccessorHeader);

                GuestShaderCacheEntry entry = new GuestShaderCacheEntry(header, code);

                if (gpuAccessor != null)
                {
                    foreach (int textureHandle in context.TextureHandlesForCache)
                    {
                        GuestTextureDescriptor textureDescriptor = ((Image.TextureDescriptor)gpuAccessor.GetTextureDescriptor(textureHandle, -1)).ToCache();

                        textureDescriptor.Handle = (uint)textureHandle;

                        entry.TextureDescriptors.Add(textureHandle, textureDescriptor);
                    }
                }

                entries[i - startIndex] = entry;
            }

            return(entries);
        }
Example #7
0
        /// <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);
        }
    }