Ejemplo n.º 1
0
        public Shader LoadShader(string name, AssetSourceEnum assetTypes, ShaderStages stage, bool useSpirvCompile = false)
        {
            //Input path should be '/' folder delimited. It is changed to '.' delimited for embedded resources

            var shaderFileInfo = GetShaderFileInfo(name, _systemComponents.Device, useSpirvCompile);

            var shaderBytes = new byte[] { };

            switch (assetTypes)
            {
            case AssetSourceEnum.File:
                shaderBytes = TryLoadShaderBytesFromFile(shaderFileInfo);
                break;

            case AssetSourceEnum.Embedded:
                shaderFileInfo.Directory = shaderFileInfo.Directory.Replace('/', '.');
                shaderBytes = TryLoadShaderBytesFromEmbeddedResource(shaderFileInfo);
                break;
            }

            if (shaderBytes.Length == 0)
            {
                _frameworkMessenger.Report(string.Concat("Unable to load shader file: ",
                                                         shaderFileInfo.Directory, "| ", shaderFileInfo.Name, " | ", shaderFileInfo.Extension,
                                                         ", of type --> ", assetTypes.ToString()));
                return(null);
            }

            return(useSpirvCompile ?
                   _systemComponents.Factory.CreateShaderCompileFromSpirv(new ShaderDescription(stage, shaderBytes, shaderFileInfo.EntryPointMethod, true)):
                   _systemComponents.Factory.CreateShader(new ShaderDescription(stage, shaderBytes, shaderFileInfo.EntryPointMethod, true)));
        }
Ejemplo n.º 2
0
        public void FontManager_LoadUserFontCheckValidFlow_MakesCorrectCalls(AssetSourceEnum source)
        {
            var id         = Substitute.For <IIdGenerator>();
            var properties = Substitute.For <IStartupPropertiesCache>();
            var collection = Substitute.For <IFontCollection>();
            var loader     = Substitute.For <IFontLoader>();

            id.New().Returns(0UL);
            collection.Add(Arg.Any <ulong>(), Arg.Any <IFontModel>()).Returns(true);

            properties.User.Returns(new StartupConfig
            {
                FontFolder = "folder"
            });

            loader.FindDotFntFileNamePartialMatchesFromEmbeddedResource(Arg.Any <bool>(), Arg.Any <string>()).Returns(new List <string> {
                "random"
            });
            loader.FindDotFntFileNamePartialMatchesFromFileResource(Arg.Any <string>()).Returns(new List <string> {
                "random"
            });

            loader.LoadEmbeddedStream(Arg.Any <bool>(), Arg.Any <string>()).Returns(default(Stream));
            loader.LoadFileStream(Arg.Any <string>()).Returns(default(Stream));

            loader.ReadStreamToStringList(Arg.Any <Stream>()).Returns(new List <string> {
                "single line"
            });

            loader.TryToLoadSubFontDescription(Arg.Any <string>(), Arg.Any <bool>(), Arg.Any <AssetSourceEnum>(), Arg.Any <ImageFormat>(), Arg.Any <List <string> >()).Returns(new CandidateSubFontDesc());

            loader.GenerateFontFromDescriptionInfo(Arg.Any <CandidateFontDesc>())
            .Returns(new Yak2D.Font.FontModel(new List <SubFont> {
                new SubFont(1, 1, null, null, false, null)
            }));

            IFontManager manager = new FontManager(id, properties, collection, loader);

            loader.ClearReceivedCalls();

            var result = manager.LoadUserFont("path", source, ImageFormat.PNG);

            Assert.NotNull(result);

            switch (source)
            {
            case AssetSourceEnum.Embedded:
                loader.Received(1).FindDotFntFileNamePartialMatchesFromEmbeddedResource(false, Arg.Any <string>());
                loader.Received(1).LoadEmbeddedStream(false, Arg.Any <string>());
                break;

            case AssetSourceEnum.File:
                loader.Received(1).FindDotFntFileNamePartialMatchesFromFileResource(Arg.Any <string>());
                loader.Received(1).LoadFileStream(Arg.Any <string>());
                break;
            }
            loader.Received(1).ReadStreamToStringList(Arg.Any <Stream>());
            loader.Received(1).TryToLoadSubFontDescription(Arg.Any <string>(), false, source, ImageFormat.PNG, Arg.Any <List <string> >());
            loader.Received(1).GenerateFontFromDescriptionInfo(Arg.Any <CandidateFontDesc>());
        }
Ejemplo n.º 3
0
 public ICustomShaderStage CreateCustomShaderStage(string fragmentShaderPathName,
                                                   AssetSourceEnum assetType,
                                                   ShaderUniformDescription[] uniformDescriptions,
                                                   BlendState blendState,
                                                   bool useSpirvCompile = false)
 {
     return(_renderStageManager.CreateCustomShaderStage(fragmentShaderPathName,
                                                        assetType,
                                                        uniformDescriptions,
                                                        blendState,
                                                        useSpirvCompile));
 }
Ejemplo n.º 4
0
        public TextureData LoadTextureColourData(string path, AssetSourceEnum assetType, ImageFormat imageFormat)
        {
            switch (assetType)
            {
            case AssetSourceEnum.File:
                return(_surfaceManager.LoadTextureColourDataFromFile(path, imageFormat));

            case AssetSourceEnum.Embedded:
                return(_surfaceManager.LoadTextureColourDataFromEmbeddedResourceInUserApplication(path, imageFormat));
            }

            return(default(TextureData));
        }
Ejemplo n.º 5
0
        public ICustomShaderStage CreateCustomShaderStage(string fragmentShaderFilename,
                                                          AssetSourceEnum assetType,
                                                          ShaderUniformDescription[] uniformDescriptions,
                                                          BlendState blendState,
                                                          bool useSpirvCompile)
        {
            var id = _idGenerator.New();

            var model = _renderStageModelFactory.CreateCustomStageModel(fragmentShaderFilename, assetType, uniformDescriptions, blendState, useSpirvCompile);

            var userReference = new CustomShaderStage(id);

            return(_renderStageCollection.Add(id, model) ? userReference : null);
        }
Ejemplo n.º 6
0
        public IFont LoadFont(string path, AssetSourceEnum assetType, ImageFormat imageFormat = ImageFormat.PNG)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                throw new Yak2DException("Fonts -> Load font passed null or empty path", new ArgumentNullException("path"));
            }

            var trimmedPath = path.Trim();

            if (trimmedPath.Any(char.IsWhiteSpace))
            {
                throw new Yak2DException("Fonts -> Asset path name cannot contain any whitespace", new ArgumentNullException("path"));
            }

            return(_fontManager.LoadUserFont(trimmedPath, assetType, imageFormat));
        }
Ejemplo n.º 7
0
        public IFont LoadUserFont(string fontPathWithoutExtension, AssetSourceEnum assetType, ImageFormat imageFormat)
        {
            //Guards and exceptions for null or white space strings already exist at the IFont Level
            if (string.IsNullOrWhiteSpace(fontPathWithoutExtension) || fontPathWithoutExtension.Trim().Any(char.IsWhiteSpace))
            {
                throw new Yak2DException(string.Concat("Unable to load user font as path is null or contains whitespace"));
            }

            fontPathWithoutExtension = fontPathWithoutExtension.Trim();

            var font = LoadFont(false, fontPathWithoutExtension, assetType, imageFormat);

            var id = _idGenerator.New();

            return(_userFontCollection.Add(id, font) ? new FontReference(id) : null);
        }
Ejemplo n.º 8
0
 public ICustomShaderStageModel CreateCustomStageModel(string fragmentShaderFilename,
                                                       AssetSourceEnum assetType,
                                                       ShaderUniformDescription[] uniformDescriptions,
                                                       BlendState blendState,
                                                       bool useSpirvCompile)
 {
     return(new CustomShaderStageModel(_frameworkMessenger,
                                       _systemComponents,
                                       _shaderTools,
                                       _pipelineFactory,
                                       _blendStateConverter,
                                       fragmentShaderFilename,
                                       assetType,
                                       uniformDescriptions,
                                       blendState,
                                       useSpirvCompile));
 }
Ejemplo n.º 9
0
        public ITexture LoadTexture(string path,
                                    AssetSourceEnum assetType,
                                    ImageFormat imageFormat = ImageFormat.PNG,
                                    SamplerType samplerType = SamplerType.Anisotropic,
                                    bool generateMipMaps    = true)
        {
            switch (assetType)
            {
            case AssetSourceEnum.File:
                return(_surfaceManager.CreateTextureFromFile(path, imageFormat, samplerType, generateMipMaps));

            case AssetSourceEnum.Embedded:
                return(_surfaceManager.CreateTextureFromEmbeddedResourceInUserApplication(path, imageFormat, samplerType, generateMipMaps));
            }

            return(null);
        }
Ejemplo n.º 10
0
        public CustomShaderStageModel(IFrameworkMessenger frameworkMessenger,
                                      ISystemComponents systemComponents,
                                      IShaderLoader shaderLoader,
                                      IPipelineFactory pipelineFactory,
                                      IBlendStateConverter blendStateConverter,
                                      string fragmentShaderFilename,
                                      AssetSourceEnum shaderFileAssetType,
                                      ShaderUniformDescription[] userUniformDescriptions,
                                      BlendState blendState,
                                      bool userSpirvCompile)
        {
            _frameworkMessenger      = frameworkMessenger;
            _systemComponents        = systemComponents;
            _shaderLoader            = shaderLoader;
            _pipelineFactory         = pipelineFactory;
            _blendStateConverter     = blendStateConverter;
            _fragmentShaderFilename  = fragmentShaderFilename;
            _shaderFileAssetType     = shaderFileAssetType;
            _userUniformDescriptions = userUniformDescriptions;
            _blendState      = blendState;
            _useSpirvCompile = userSpirvCompile;

            Initialise();
        }
Ejemplo n.º 11
0
        /*
         *    Shader File Configuration Notes
         *
         *    A small number of naming, file extension and entry point restrictions are mandated to enable easy use
         *    of shaders across each backend.
         *
         *    Shader Files
         *    - One file should be used for each Vertex and Fragment Shader
         *    - Each Vertex shader code file MUST have "Vertex" in the name
         *    - Each Fragment shader code file MUST have "Fragment" in the name
         *    - Shaders for each backend sit in their respective sub-folders (Direct3D, Metal, OpenGL and Vulkan)
         *
         *    - All HLSL shader source files should have extension .hlsl
         *    - The HLSL compiling script uses the presence of Vertex/Fragment in the source file name to choose the right compiling profile
         *    - Compiled HLSL code files loaded by the framework have the extension .hlsl.bytes
         *
         *    - Vulkan Shaders must use the extensions .vert and .frag respectively for Vertex and Fragment shaders
         *    - Vulkan shader code is compiled to .spv
         *
         *    - OpenGL Shaders must all use the extension .glsl
         *    - OpenGL shader code is not compiled, and loaded as text byte strings straight from shader source during runtime
         *
         *    - Metal shader source files have the extension .metal
         *    - Metal shaders are compiled to .metallib
         *
         *    Shader Code
         *    - The entry point for all Vertex and Pixel Shader functions across backends is main()
         *    - EXCEPT metal, where main() is not permitted. For metal shaders the function shader() is used
         *
         *    SPIR-V Additions
         *    - The main framework avoids relying on SPIR-V compilation from single source due to inconsistencies that can crop up
         *      either due to user error, SPIR-V issue or backend unique properties. This caution maybe unwarranted, but there was
         *      a preference to code each backend shader individually
         *    - HOWEVER - for custom shaders stages, a SPIR-V .glsl can be used, enabling runtime cross platform compilation
         *    - The rules for SPIR-V shaders are 1. paths are rooted in Shaders/ base directory, .glsl extension and main() entiry point
         *
         *    Notes:
         * You may note significant padding in shader uniforms, and the avoidance of some vector types, particularly float/vec3s
         *    - Most of this is overly defensive and ignores potential reliance that could be had on explicit byte layout guides provided in c# structs
         *    - The aim here was to avoid the stride differences found in some data types across backends, reducing/removing uniform data problems
         * Compiling HLSL with DXC.exe (the latest compiler) causes issues with SharpDX that are quite opague. Compiling with FXC.exe
         *    - works for included shaders and is therefore the preferred method. In the future I would like to find out why this issue exists (semantics?)
         * I avoided using Veldrid-SPIRV cross for shader translation across backends to avoid edge case problems,
         *    - remove another layer of potential debugging, and also to better learn other shader languages
         *    - With the shader set relatively fixed, it could be ported to single source, cross translation in the future
         *
         *    Shader Compilation
         * Scripts are provided for compiling all shaders in a directory: .bat file for windows HLSL/Vulkan and .sh for Linux Vulkan and OSX metal
         * .bat files must be given one argument, which is the filepath of the compiler .exe to use
         * .sh shell scripts relay on compiler paths being present on the system
         * NOTE: Currently, the .bat files are able to hand recursive directories of shaders to compile
         * .sh shell scripts currently only compile shaders in same directory as the script (to be modified in the future)
         *
         *    Compilers
         * HLSL: Use FXC.exe (I experience issues with DXC.exe)
         * Vulkan: Use glslc from the Vulkan SDK
         * Metal: Use xcrun (will require installation of XCode and associated tools)
         */

        public ShaderPackage CreateShaderPackage(string vertexShaderName,
                                                 AssetSourceEnum vertexShaderAssetType,
                                                 string fragmentShaderName,
                                                 AssetSourceEnum fragmentShaderAssetType,
                                                 VertexLayoutDescription layoutDescription,
                                                 ResourceLayoutElementDescription[][] uniformDescriptions,
                                                 bool useSpirvCompileVertexShader   = false,
                                                 bool useSpirvCompileFragmentShader = false)
        {
            if (uniformDescriptions == null)
            {
                throw new Yak2DException("Error loading shader, null uniform resource layout descriptions array provided");
            }

            for (var n = 0; n < uniformDescriptions.Length; n++)
            {
                if (uniformDescriptions[n] == null)
                {
                    throw new Yak2DException("Error loading shader, null uniform resource layout descriptions sub-array provided");
                }
            }

            if (vertexShaderName == null || vertexShaderName.Trim().Any(char.IsWhiteSpace))
            {
                throw new Yak2DException("Error loading shader, Vertex Shader name cannot be null or contain whitespace");
            }
            vertexShaderName = vertexShaderName.Trim();

            if (fragmentShaderName == null || fragmentShaderName.Trim().Any(char.IsWhiteSpace))
            {
                throw new Yak2DException("Error loading shader, Fragment Shader name cannot be null or contain whitespace");
            }
            fragmentShaderName = fragmentShaderName.Trim();

            var uniformResourceLayout = _loadFunctions.CreateUniformResourceLayouts(uniformDescriptions);

            var vertexShader = _loadFunctions.LoadShader(vertexShaderName, vertexShaderAssetType, ShaderStages.Vertex, useSpirvCompileVertexShader);

            if (vertexShader == null)
            {
                throw new Yak2DException(string.Concat("Vertex Shader: ", vertexShaderName, " failed to Load"));
            }

            var fragmentShader = _loadFunctions.LoadShader(fragmentShaderName, fragmentShaderAssetType, ShaderStages.Fragment, useSpirvCompileFragmentShader);

            if (fragmentShader == null)
            {
                throw new Yak2DException(string.Concat("Fragment Shader: ", fragmentShaderName, " failed to Load"));
            }

            return(new ShaderPackage
            {
                Description = new ShaderSetDescription(
                    new[]
                {
                    layoutDescription
                },
                    new[]
                {
                    vertexShader,
                    fragmentShader
                }
                    ),
                UniformResourceLayout = uniformResourceLayout
            });
        }
Ejemplo n.º 12
0
        private IFontModel LoadFont(bool isFrameworkInternal, string pathToFontNameNoExtension, AssetSourceEnum assetType, ImageFormat imageFormat)
        {
            //There is a .fnt file for each size of a font. The .fnt file contains information on spacing and importantly,
            //what the texture files are called for that font size. When loading a font, we attempt to load each font size
            //as an additional subfont. Hence here, we seach for font name partial/leading matches. i.e.
            //"notosans" would find font sizes such as "notosans_22.fnt", "notosans_24.fnt", etc. We then try and load each
            //of those font files as a sub font. The seach path may include additional folders, such as "/mono/notosans" etc
            //We always match the entire search string with the leading part of the font name

            var pathWithoutAssemblyOrExtension = string.Concat(isFrameworkInternal ? _systemFontFolder : _startUpProperties.FontFolder,
                                                               isFrameworkInternal ? "." : "/",
                                                               pathToFontNameNoExtension);

            pathWithoutAssemblyOrExtension = assetType == AssetSourceEnum.Embedded ? pathWithoutAssemblyOrExtension.Replace("/", ".") :
                                             pathWithoutAssemblyOrExtension.Replace(".", "/");

            var fontBaseFolderWithoutAssemblyDoesNotEndInDivisor =
                assetType == AssetSourceEnum.Embedded ? pathWithoutAssemblyOrExtension.Substring(0, pathWithoutAssemblyOrExtension.LastIndexOf(".")) :
                pathWithoutAssemblyOrExtension.Substring(0, pathWithoutAssemblyOrExtension.LastIndexOf("/"));

            var candidateSubFontDotFntResourceFileNames = new List <string> {
            };

            switch (assetType)
            {
            case AssetSourceEnum.Embedded:
                candidateSubFontDotFntResourceFileNames = _fontLoader.FindDotFntFileNamePartialMatchesFromEmbeddedResource(isFrameworkInternal, pathWithoutAssemblyOrExtension);
                break;

            case AssetSourceEnum.File:
                candidateSubFontDotFntResourceFileNames = _fontLoader.FindDotFntFileNamePartialMatchesFromFileResource(pathWithoutAssemblyOrExtension);
                break;
            }

            if (candidateSubFontDotFntResourceFileNames.Count == 0)
            {
                throw new Yak2DException(string.Concat("Unable to complete Load Font request, no potential .fnt resources were found: ",
                                                       pathWithoutAssemblyOrExtension, " , ", assetType.ToString(), ",",
                                                       " Framework Internal?== ", isFrameworkInternal));
            }

            candidateSubFontDotFntResourceFileNames.ForEach(x =>
            {
                if (x.Any(char.IsWhiteSpace))
                {
                    throw new Yak2DException(string.Concat("Unable to complete Load Font request, resource names must not contain whitespace: ", x));
                }
                ;
            });

            //These LINQs could be collapsed, but split out for now to aid reading
            var candidateSubFontDotFntFileStreams = candidateSubFontDotFntResourceFileNames.Select(resourcePathName =>
            {
                Stream stream = null;

                //Switch rather than one line conditional in case future requires other content types
                switch (assetType)
                {
                case AssetSourceEnum.Embedded:
                    stream = _fontLoader.LoadEmbeddedStream(isFrameworkInternal, resourcePathName);
                    break;

                case AssetSourceEnum.File:
                    stream = _fontLoader.LoadFileStream(resourcePathName);
                    break;
                }

                return(stream);
            }).ToList();

            var streamStrings = candidateSubFontDotFntFileStreams.Select(stream =>
            {
                return(_fontLoader.ReadStreamToStringList(stream));
            }).Where(lines => lines.Count > 0).ToList();

            var candidateFontDesc = new CandidateFontDesc
            {
                Name = pathToFontNameNoExtension,
                CandidateSubFonts = streamStrings.Select(lines => _fontLoader.TryToLoadSubFontDescription(
                                                             fontBaseFolderWithoutAssemblyDoesNotEndInDivisor,
                                                             isFrameworkInternal,
                                                             assetType,
                                                             imageFormat,
                                                             lines)).Where(desc => desc != null).ToList()
            };

            if (candidateFontDesc.CandidateSubFonts.Count == 0)
            {
                throw new Yak2DException(string.Concat(string.Concat("Unable to complete font request, failed to load any sub font descriptors: ",
                                                                     pathToFontNameNoExtension, " , ", assetType.ToString(), ",",
                                                                     " Framework Internal?== ", isFrameworkInternal)));
            }

            return(_fontLoader.GenerateFontFromDescriptionInfo(candidateFontDesc));
        }
Ejemplo n.º 13
0
        public CandidateSubFontDesc TryToLoadSubFontDescription(string fontBaseFolderWithoutAssemblyDoesNotEndInDivisor,
                                                                bool isFrameworkInternal,
                                                                AssetSourceEnum assetType,
                                                                ImageFormat imageFormat,
                                                                List <string> fntFileLines)
        {
            var resourceFolder = fontBaseFolderWithoutAssemblyDoesNotEndInDivisor;

            var extractedTexturePathsWithoutBasePath = _subFontGenerator.ExtractPngFilePathsFromDotFntLines(fntFileLines);

            if (extractedTexturePathsWithoutBasePath.Count == 0)
            {
                return(null); //Fail Message already displayed in earlier method
            }

            var texturePathsWithoutBasePathsWithoutFileExtension = extractedTexturePathsWithoutBasePath.Select(x =>
            {
                if (!x.EndsWith(".png"))
                {
                    _frameworkMessenger.Report("Warning -> expected texture path name does not end with .png");
                }

                return(x.Remove(x.Length - 4, 4));
            }).ToList();

            var textures = new List <ITexture>();

            texturePathsWithoutBasePathsWithoutFileExtension.ForEach(texturePath =>
            {
                ITexture texture = null;
                var texturePathWithoutExtension = string.Concat(resourceFolder, assetType == AssetSourceEnum.Embedded ? "." : "/", texturePath);

                switch (assetType)
                {
                case AssetSourceEnum.Embedded:
                    texture = _gpuSurfaceManager.CreateFontTextureFromEmbeddedResource(isFrameworkInternal,
                                                                                       texturePathWithoutExtension,
                                                                                       imageFormat,
                                                                                       SamplerType.Anisotropic);
                    break;

                case AssetSourceEnum.File:
                    texture = _gpuSurfaceManager.CreateFontTextureFromFile(texturePathWithoutExtension, imageFormat, SamplerType.Anisotropic);
                    break;
                }
                if (texture != null)
                {
                    textures.Add(texture);
                }
            });

            if (textures.Count == 0)
            {
                _frameworkMessenger.Report(string.Concat("Texture loads for font failed: ",
                                                         resourceFolder, ",",
                                                         " Framework Internal?== ", isFrameworkInternal));
            }

            return(new CandidateSubFontDesc
            {
                DotFntLines = fntFileLines,
                TexturePaths = texturePathsWithoutBasePathsWithoutFileExtension,
                Textures = textures
            });
        }