using System.Text;
namespace Veldrid.SPIRV
{
///
/// Contains extension methods for loading modules from SPIR-V bytecode.
///
public static class ResourceFactoryExtensions
{
///
/// Creates a vertex and fragment shader pair from the given pair containing SPIR-V
/// bytecode or GLSL source code.
///
/// The used to compile the translated shader code.
/// The vertex shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which can be compiled to SPIR-V.
/// The fragment shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which
/// can be compiled to SPIR-V.
/// A two-element array, containing the vertex shader (element 0) and the fragment shader (element 1).
public static Shader[] CreateFromSpirv(
this ResourceFactory factory,
ShaderDescription vertexShaderDescription,
ShaderDescription fragmentShaderDescription)
{
return CreateFromSpirv(factory, vertexShaderDescription, fragmentShaderDescription, new CrossCompileOptions());
}
///
/// Creates a vertex and fragment shader pair from the given pair containing SPIR-V
/// bytecode or GLSL source code.
///
/// The used to compile the translated shader code.
/// The vertex shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which can be compiled to SPIR-V.
/// The fragment shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which
/// can be compiled to SPIR-V.
/// The which will control the parameters used to translate the
/// shaders from SPIR-V to the target language.
/// A two-element array, containing the vertex shader (element 0) and the fragment shader (element 1).
public static Shader[] CreateFromSpirv(
this ResourceFactory factory,
ShaderDescription vertexShaderDescription,
ShaderDescription fragmentShaderDescription,
CrossCompileOptions options)
{
GraphicsBackend backend = factory.BackendType;
if (backend == GraphicsBackend.Vulkan)
{
vertexShaderDescription.ShaderBytes = EnsureSpirv(vertexShaderDescription);
fragmentShaderDescription.ShaderBytes = EnsureSpirv(fragmentShaderDescription);
return new Shader[]
{
factory.CreateShader(ref vertexShaderDescription),
factory.CreateShader(ref fragmentShaderDescription)
};
}
CrossCompileTarget target = GetCompilationTarget(factory.BackendType);
VertexFragmentCompilationResult compilationResult = SpirvCompilation.CompileVertexFragment(
vertexShaderDescription.ShaderBytes,
fragmentShaderDescription.ShaderBytes,
target,
options);
string vertexEntryPoint = (backend == GraphicsBackend.Metal && vertexShaderDescription.EntryPoint == "main")
? "main0"
: vertexShaderDescription.EntryPoint;
byte[] vertexBytes = GetBytes(backend, compilationResult.VertexShader);
Shader vertexShader = factory.CreateShader(new ShaderDescription(
vertexShaderDescription.Stage,
vertexBytes,
vertexEntryPoint));
string fragmentEntryPoint = (backend == GraphicsBackend.Metal && fragmentShaderDescription.EntryPoint == "main")
? "main0"
: fragmentShaderDescription.EntryPoint;
byte[] fragmentBytes = GetBytes(backend, compilationResult.FragmentShader);
Shader fragmentShader = factory.CreateShader(new ShaderDescription(
fragmentShaderDescription.Stage,
fragmentBytes,
fragmentEntryPoint));
return new Shader[] { vertexShader, fragmentShader };
}
///
///
///
///
///
///
public static void CompileFromSpirv(ref ShaderDescription vertex,ref ShaderDescription fragment,GraphicsBackend backend = GraphicsBackend.Vulkan)
{
CompileFromSpirv(ref vertex, ref fragment, new CrossCompileOptions(), backend);
}
///
///
///
///
///
///
///
public static void CompileFromSpirv(ref ShaderDescription vertex, ref ShaderDescription fragment,CrossCompileOptions options, GraphicsBackend backend = GraphicsBackend.Vulkan)
{
if (backend == GraphicsBackend.Vulkan)
{
vertex.ShaderBytes = EnsureSpirv(vertex);
fragment.ShaderBytes = EnsureSpirv(fragment);
return;
}
CrossCompileTarget target = GetCompilationTarget(backend);
VertexFragmentCompilationResult compilationResult = SpirvCompilation.CompileVertexFragment(
vertex.ShaderBytes,
fragment.ShaderBytes,
target,
options);
vertex.EntryPoint = (backend == GraphicsBackend.Metal && vertex.EntryPoint == "main")
? "main0"
: vertex.EntryPoint;
vertex.ShaderBytes = GetBytes(backend, compilationResult.VertexShader);
fragment.EntryPoint = (backend == GraphicsBackend.Metal && fragment.EntryPoint == "main")
? "main0"
: fragment.EntryPoint;
fragment.ShaderBytes = GetBytes(backend, compilationResult.FragmentShader);
}
///
/// Creates a compute shader from the given containing SPIR-V bytecode or GLSL source
/// code.
///
/// The used to compile the translated shader code.
/// The compute shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which
/// can be compiled to SPIR-V.
/// The compiled compute .
public static Shader CreateFromSpirv(
this ResourceFactory factory,
ShaderDescription computeShaderDescription)
{
return CreateFromSpirv(factory, computeShaderDescription, new CrossCompileOptions());
}
///
/// Creates a compute shader from the given containing SPIR-V bytecode or GLSL source
/// code.
///
/// The used to compile the translated shader code.
/// The compute shader's description.
/// should contain SPIR-V bytecode or Vulkan-style GLSL source code which
/// can be compiled to SPIR-V.
/// The which will control the parameters used to translate the
/// shaders from SPIR-V to the target language.
/// The compiled compute .
public static Shader CreateFromSpirv(
this ResourceFactory factory,
ShaderDescription computeShaderDescription,
CrossCompileOptions options)
{
GraphicsBackend backend = factory.BackendType;
if (backend == GraphicsBackend.Vulkan)
{
computeShaderDescription.ShaderBytes = EnsureSpirv(computeShaderDescription);
return factory.CreateShader(ref computeShaderDescription);
}
CrossCompileTarget target = GetCompilationTarget(factory.BackendType);
ComputeCompilationResult compilationResult = SpirvCompilation.CompileCompute(
computeShaderDescription.ShaderBytes,
target,
options);
string computeEntryPoint = (backend == GraphicsBackend.Metal && computeShaderDescription.EntryPoint == "main")
? "main0"
: computeShaderDescription.EntryPoint;
byte[] computeBytes = GetBytes(backend, compilationResult.ComputeShader);
return factory.CreateShader(new ShaderDescription(
computeShaderDescription.Stage,
computeBytes,
computeEntryPoint));
}
private static unsafe byte[] EnsureSpirv(ShaderDescription description)
{
if (Util.HasSpirvHeader(description.ShaderBytes))
{
return description.ShaderBytes;
}
else
{
fixed (byte* sourceAsciiPtr = description.ShaderBytes)
{
SpirvCompilationResult glslCompileResult = SpirvCompilation.CompileGlslToSpirv(
(uint)description.ShaderBytes.Length,
sourceAsciiPtr,
null,
description.Stage,
description.Debug,
0,
null);
return glslCompileResult.SpirvBytes;
}
}
}
private static byte[] GetBytes(GraphicsBackend backend, string code)
{
switch (backend)
{
case GraphicsBackend.Direct3D11:
case GraphicsBackend.OpenGL:
case GraphicsBackend.OpenGLES:
return Encoding.ASCII.GetBytes(code);
case GraphicsBackend.Metal:
return Encoding.UTF8.GetBytes(code);
default:
throw new SpirvCompilationException($"Invalid GraphicsBackend: {backend}");
}
}
private static CrossCompileTarget GetCompilationTarget(GraphicsBackend backend)
{
switch (backend)
{
case GraphicsBackend.Direct3D11:
return CrossCompileTarget.HLSL;
case GraphicsBackend.OpenGL:
return CrossCompileTarget.GLSL;
case GraphicsBackend.Metal:
return CrossCompileTarget.MSL;
case GraphicsBackend.OpenGLES:
return CrossCompileTarget.ESSL;
default:
throw new SpirvCompilationException($"Invalid GraphicsBackend: {backend}");
}
}
}
}