You're breathtaking!
This commit is contained in:
72
Source/Engine/ShadersCompilation/Config.h
Normal file
72
Source/Engine/ShadersCompilation/Config.h
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Graphics/Shaders/Config.h"
|
||||
|
||||
class MemoryWriteStream;
|
||||
|
||||
/// <summary>
|
||||
/// Shader compilation options container
|
||||
/// </summary>
|
||||
struct FLAXENGINE_API ShaderCompilationOptions
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Name of the target object (name of the shader or material for better logging readability)
|
||||
/// </summary>
|
||||
String TargetName;
|
||||
|
||||
/// <summary>
|
||||
/// Unique ID of the target object
|
||||
/// </summary>
|
||||
Guid TargetID = Guid::Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Shader source code (null terminated)
|
||||
/// </summary>
|
||||
const char* Source = nullptr;
|
||||
|
||||
/// <summary>
|
||||
/// Shader source code length
|
||||
/// </summary>
|
||||
uint32 SourceLength = 0;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Target shader profile
|
||||
/// </summary>
|
||||
ShaderProfile Profile = ShaderProfile::Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time.
|
||||
/// </summary>
|
||||
bool NoOptimize = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enables shader debug data generation (depends on the target platform rendering backend).
|
||||
/// </summary>
|
||||
bool GenerateDebugData = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enable/disable promoting warnings to compilation errors
|
||||
/// </summary>
|
||||
bool TreatWarningsAsErrors = false;
|
||||
|
||||
/// <summary>
|
||||
/// Custom macros for the shader compilation
|
||||
/// </summary>
|
||||
Array<ShaderMacro> Macros;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Output stream to write compiled shader cache to
|
||||
/// </summary>
|
||||
MemoryWriteStream* Output = nullptr;
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// DirectX shaders compiler module using D3DCompiler.
|
||||
/// </summary>
|
||||
public class ShaderCompilerD3D : ShaderCompiler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.SourceFiles.Clear();
|
||||
options.SourcePaths.Clear();
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "ShaderCompilerD3D.h"));
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "ShaderCompilerD3D.cpp"));
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_D3D_SHADER_COMPILER");
|
||||
|
||||
var depsRoot = options.DepsFolder;
|
||||
options.OutputFiles.Add("dxguid.lib");
|
||||
options.OutputFiles.Add("d3dcompiler.lib");
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "d3dcompiler_47.dll"));
|
||||
options.DelayLoadLibraries.Add("d3dcompiler_47.dll");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
base.GetFilesToDeploy(files);
|
||||
|
||||
files.Add(Path.Combine(FolderPath, "ShaderCompilerD3D.h"));
|
||||
}
|
||||
}
|
||||
351
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp
Normal file
351
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_D3D_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilerD3D.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Graphics/Config.h"
|
||||
#include "Engine/GraphicsDevice/DirectX/IncludeDirectXHeaders.h"
|
||||
#include "Engine/Platform/Windows/ComPtr.h"
|
||||
#include <d3dcompiler.h>
|
||||
|
||||
/// <summary>
|
||||
/// Helper class to include source for D3D shaders compiler.
|
||||
/// </summary>
|
||||
class IncludeD3D : public ID3DInclude
|
||||
{
|
||||
private:
|
||||
|
||||
ShaderCompilationContext* _context;
|
||||
|
||||
public:
|
||||
|
||||
IncludeD3D(ShaderCompilationContext* context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID* ppData, UINT* pBytes) override
|
||||
{
|
||||
const char* source;
|
||||
int32 sourceLength;
|
||||
const StringAnsi filename(pFileName);
|
||||
if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength))
|
||||
return E_FAIL;
|
||||
*ppData = source;
|
||||
*pBytes = sourceLength;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE Close(LPCVOID pData) override
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
ShaderCompilerD3D::ShaderCompilerD3D(ShaderProfile profile)
|
||||
: ShaderCompiler(profile)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef GPU_USE_SHADERS_DEBUG_LAYER
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ProcessDebugInfo(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, void* srcData, uint32 srcDataSize)
|
||||
{
|
||||
// TODO: test with D3D_DISASM_ENABLE_INSTRUCTION_CYCLE option
|
||||
|
||||
ComPtr<ID3DBlob> debug;
|
||||
HRESULT result = D3DDisassemble(srcData, srcDataSize, D3D_DISASM_ENABLE_DEFAULT_VALUE_PRINTS | D3D_DISASM_ENABLE_INSTRUCTION_NUMBERING, nullptr, &debug);
|
||||
if (FAILED(result))
|
||||
{
|
||||
LOG(Warning, "DirectX error: {0} at {1}:{2}", result, TEXT(__FILE__), __LINE__);
|
||||
context->OnError("D3DDisassemble failed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract debug info
|
||||
char* debugData = static_cast<char*>(debug->GetBufferPointer());
|
||||
const int32 debugDataSize = static_cast<int32>(debug->GetBufferSize());
|
||||
context->OnCollectDebugInfo(meta, permutationIndex, debugData, debugDataSize);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ProcessShader(ShaderCompilationContext* context, Array<ShaderCompiler::ShaderResourceBuffer>& constantBuffers, ID3D11ShaderReflection* reflector, D3D11_SHADER_DESC& desc, ShaderBindings& bindings)
|
||||
{
|
||||
// Extract constant buffers usage information
|
||||
for (uint32 a = 0; a < desc.ConstantBuffers; a++)
|
||||
{
|
||||
// Get CB
|
||||
auto cb = reflector->GetConstantBufferByIndex(a);
|
||||
|
||||
// Get CB description
|
||||
D3D11_SHADER_BUFFER_DESC cbDesc;
|
||||
cb->GetDesc(&cbDesc);
|
||||
|
||||
// Check buffer type
|
||||
if (cbDesc.Type == D3D_CT_CBUFFER)
|
||||
{
|
||||
// Find CB slot index
|
||||
int32 slot = INVALID_INDEX;
|
||||
for (uint32 b = 0; b < desc.BoundResources; b++)
|
||||
{
|
||||
D3D11_SHADER_INPUT_BIND_DESC bDesc;
|
||||
reflector->GetResourceBindingDesc(b, &bDesc);
|
||||
if (StringUtils::Compare(bDesc.Name, cbDesc.Name) == 0)
|
||||
{
|
||||
slot = bDesc.BindPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot == INVALID_INDEX)
|
||||
{
|
||||
context->OnError("Missing bound resource.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set flag
|
||||
bindings.UsedCBsMask |= 1 << slot;
|
||||
|
||||
// Try to add CB to the list
|
||||
for (int32 b = 0; b < constantBuffers.Count(); b++)
|
||||
{
|
||||
auto& cc = constantBuffers[b];
|
||||
if (cc.Slot == slot)
|
||||
{
|
||||
cc.IsUsed = true;
|
||||
cc.Size = cbDesc.Size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract resources usage
|
||||
for (uint32 i = 0; i < desc.BoundResources; i++)
|
||||
{
|
||||
// Get resource description
|
||||
D3D11_SHADER_INPUT_BIND_DESC resDesc;
|
||||
reflector->GetResourceBindingDesc(i, &resDesc);
|
||||
|
||||
switch (resDesc.Type)
|
||||
{
|
||||
// Sampler
|
||||
case D3D_SIT_SAMPLER:
|
||||
break;
|
||||
|
||||
// Constant Buffer
|
||||
case D3D_SIT_CBUFFER:
|
||||
case D3D_SIT_TBUFFER:
|
||||
break;
|
||||
|
||||
// Shader Resource
|
||||
case D3D_SIT_TEXTURE:
|
||||
case D3D_SIT_STRUCTURED:
|
||||
case D3D_SIT_BYTEADDRESS:
|
||||
bindings.UsedSRsMask |= 1 << resDesc.BindPoint;
|
||||
break;
|
||||
|
||||
// Unordered Access
|
||||
case D3D_SIT_UAV_RWTYPED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED:
|
||||
case D3D_SIT_UAV_RWBYTEADDRESS:
|
||||
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||
bindings.UsedUAsMask |= 1 << resDesc.BindPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite)
|
||||
{
|
||||
if (WriteShaderFunctionBegin(_context, meta))
|
||||
return true;
|
||||
|
||||
// Prepare
|
||||
auto options = _context->Options;
|
||||
auto type = meta.GetStage();
|
||||
IncludeD3D include(_context);
|
||||
StringAnsi profileName;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
profileName = "vs";
|
||||
break;
|
||||
case ShaderStage::Hull:
|
||||
profileName = "hs";
|
||||
break;
|
||||
case ShaderStage::Domain:
|
||||
profileName = "ds";
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
profileName = "gs";
|
||||
break;
|
||||
case ShaderStage::Pixel:
|
||||
profileName = "ps";
|
||||
break;
|
||||
case ShaderStage::Compute:
|
||||
profileName = "cs";
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
if (_profile == ShaderProfile::DirectX_SM5)
|
||||
{
|
||||
profileName += "_5_0";
|
||||
}
|
||||
else
|
||||
{
|
||||
profileName += "_4_0";
|
||||
|
||||
if (type == ShaderStage::Domain || type == ShaderStage::Hull)
|
||||
{
|
||||
_context->OnError("Tessellation is not supported on DirectX 10");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Compile all shader function permutations
|
||||
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
|
||||
{
|
||||
_macros.Clear();
|
||||
|
||||
// Get function permutation macros
|
||||
meta.GetDefinitionsForPermutation(permutationIndex, _macros);
|
||||
|
||||
// Add additional define for compiled function name
|
||||
GetDefineForFunction(meta, _macros);
|
||||
|
||||
// Add custom and global macros (global last because contain null define to indicate ending)
|
||||
_macros.Add(_context->Options->Macros);
|
||||
_macros.Add(_globalMacros);
|
||||
|
||||
// Compile
|
||||
ComPtr<ID3DBlob> errors;
|
||||
ComPtr<ID3DBlob> shader;
|
||||
HRESULT result = D3DCompile2(
|
||||
options->Source,
|
||||
options->SourceLength,
|
||||
_context->TargetNameAnsi,
|
||||
reinterpret_cast<const D3D_SHADER_MACRO*>(_macros.Get()),
|
||||
&include,
|
||||
meta.Name.Get(),
|
||||
profileName.Get(),
|
||||
_flags,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
&shader,
|
||||
&errors);
|
||||
|
||||
// Check compilation result
|
||||
if (FAILED(result))
|
||||
{
|
||||
const auto msg = static_cast<const char*>(errors->GetBufferPointer());
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
void* shaderBuffer = shader->GetBufferPointer();
|
||||
uint32 shaderBufferSize = static_cast<int32>(shader->GetBufferSize());
|
||||
|
||||
// Perform shader reflection
|
||||
ComPtr<ID3D11ShaderReflection> reflector;
|
||||
result = D3DReflect(shader->GetBufferPointer(), shader->GetBufferSize(), IID_ID3D11ShaderReflection, (void**)&reflector);
|
||||
if (FAILED(result))
|
||||
{
|
||||
LOG(Warning, "DirectX error: {0} at {1}:{2}", result, TEXT(__FILE__), __LINE__);
|
||||
_context->OnError("D3DReflect failed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get shader description
|
||||
D3D11_SHADER_DESC desc;
|
||||
reflector->GetDesc(&desc);
|
||||
|
||||
// Process shader reflection data
|
||||
ShaderBindings bindings = { desc.InstructionCount, 0, 0, 0 };
|
||||
if (ProcessShader(_context, _constantBuffers, reflector.Get(), desc, bindings))
|
||||
return true;
|
||||
|
||||
#ifdef GPU_USE_SHADERS_DEBUG_LAYER
|
||||
|
||||
// Generate debug information
|
||||
if (ProcessDebugInfo(_context, meta, permutationIndex, shaderBuffer, shaderBufferSize))
|
||||
return true;
|
||||
|
||||
#endif
|
||||
|
||||
// Strip shader bytecode for an optimization
|
||||
ComPtr<ID3DBlob> shaderStripped;
|
||||
if (!options->GenerateDebugData)
|
||||
{
|
||||
//auto prevShaderBufferSize = shaderBufferSize;
|
||||
|
||||
// Strip shader bytes
|
||||
result = D3DStripShader(
|
||||
shaderBuffer,
|
||||
shaderBufferSize,
|
||||
D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO | D3DCOMPILER_STRIP_TEST_BLOBS,
|
||||
&shaderStripped);
|
||||
if (FAILED(result))
|
||||
{
|
||||
LOG(Warning, "Cannot strip shader.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set new buffer
|
||||
shaderBuffer = shaderStripped->GetBufferPointer();
|
||||
shaderBufferSize = static_cast<int32>(shaderStripped->GetBufferSize());
|
||||
|
||||
//auto strippedBytes = prevShaderBufferSize - shaderBufferSize;
|
||||
//auto strippedBytesPercentage = Math::FloorToInt(strippedBytes * 100.0f / prevShaderBufferSize);
|
||||
}
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, shaderBuffer, shaderBufferSize))
|
||||
return true;
|
||||
|
||||
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
|
||||
return true;
|
||||
}
|
||||
|
||||
return WriteShaderFunctionEnd(_context, meta);
|
||||
}
|
||||
|
||||
bool ShaderCompilerD3D::OnCompileBegin()
|
||||
{
|
||||
if (ShaderCompiler::OnCompileBegin())
|
||||
return true;
|
||||
|
||||
_globalMacros.Add({ "DIRECTX", "1" });
|
||||
|
||||
_flags = 0;
|
||||
if (_context->Options->NoOptimize)
|
||||
_flags |= D3DCOMPILE_SKIP_OPTIMIZATION;
|
||||
else
|
||||
_flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
|
||||
if (_context->Options->GenerateDebugData)
|
||||
_flags |= D3DCOMPILE_DEBUG;
|
||||
if (_context->Options->TreatWarningsAsErrors)
|
||||
_flags |= D3DCOMPILE_WARNINGS_ARE_ERRORS;
|
||||
#if GRAPHICS_API_DIRECTX12
|
||||
_flags |= D3DCOMPILE_ALL_RESOURCES_BOUND;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
34
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.h
Normal file
34
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_D3D_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/ShaderCompiler.h"
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for DirectX platform using D3DCompiler.
|
||||
/// </summary>
|
||||
class ShaderCompilerD3D : public ShaderCompiler
|
||||
{
|
||||
private:
|
||||
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
uint32 _flags;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompilerD3D"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompilerD3D(ShaderProfile profile);
|
||||
|
||||
protected:
|
||||
|
||||
// [ShaderCompiler]
|
||||
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
|
||||
bool OnCompileBegin() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// DirectX shaders compiler module using https://github.com/microsoft/DirectXShaderCompiler.
|
||||
/// </summary>
|
||||
public class ShaderCompilerDX : ShaderCompiler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.SourceFiles.Clear();
|
||||
options.SourcePaths.Clear();
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "ShaderCompilerDX.h"));
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "ShaderCompilerDX.cpp"));
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_DX_SHADER_COMPILER");
|
||||
|
||||
var depsRoot = options.DepsFolder;
|
||||
options.OutputFiles.Add("dxcompiler.lib");
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "dxcompiler.dll"));
|
||||
options.DependencyFiles.Add(Path.Combine(depsRoot, "dxil.dll"));
|
||||
options.DelayLoadLibraries.Add("dxcompiler.dll");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
base.GetFilesToDeploy(files);
|
||||
|
||||
files.Add(Path.Combine(FolderPath, "ShaderCompilerDX.h"));
|
||||
}
|
||||
}
|
||||
403
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp
Normal file
403
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_DX_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilerDX.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Graphics/Config.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
|
||||
#include "Engine/Platform/Windows/ComPtr.h"
|
||||
#include <d3d12shader.h>
|
||||
#include <ThirdParty/DirectXShaderCompiler/dxcapi.h>
|
||||
|
||||
#ifndef DXIL_FOURCC
|
||||
#define DXIL_FOURCC(ch0, ch1, ch2, ch3) ((uint32)(uint8)(ch0) | (uint32)(uint8)(ch1) << 8 | (uint32)(uint8)(ch2) << 16 | (uint32)(uint8)(ch3) << 24)
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Helper class to include source for DX shaders compiler.
|
||||
/// </summary>
|
||||
class IncludeDX : public IDxcIncludeHandler
|
||||
{
|
||||
private:
|
||||
|
||||
ShaderCompilationContext* _context;
|
||||
IDxcLibrary* _library;
|
||||
|
||||
public:
|
||||
|
||||
IncludeDX(ShaderCompilationContext* context, IDxcLibrary* library)
|
||||
{
|
||||
_context = context;
|
||||
_library = library;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) override
|
||||
{
|
||||
if (riid == __uuidof(IDxcIncludeHandler) || riid == __uuidof(IUnknown))
|
||||
{
|
||||
AddRef();
|
||||
*ppvObject = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
*ppvObject = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE LoadSource(_In_ LPCWSTR pFilename, _COM_Outptr_result_maybenull_ IDxcBlob** ppIncludeSource) override
|
||||
{
|
||||
*ppIncludeSource = nullptr;
|
||||
const char* source;
|
||||
int32 sourceLength;
|
||||
const StringAnsi filename(pFilename);
|
||||
if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength))
|
||||
return E_FAIL;
|
||||
IDxcBlobEncoding* textBlob;
|
||||
if (FAILED(_library->CreateBlobWithEncodingFromPinned((LPBYTE)source, sourceLength, CP_UTF8, &textBlob)))
|
||||
return E_FAIL;
|
||||
*ppIncludeSource = textBlob;
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile)
|
||||
: ShaderCompiler(profile)
|
||||
{
|
||||
IDxcCompiler2* compiler = nullptr;
|
||||
IDxcLibrary* library = nullptr;
|
||||
IDxcContainerReflection* containerReflection = nullptr;
|
||||
if (FAILED(DxcCreateInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast<void**>(&compiler))) ||
|
||||
FAILED(DxcCreateInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast<void**>(&library))) ||
|
||||
FAILED(DxcCreateInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast<void**>(&containerReflection))))
|
||||
{
|
||||
LOG(Error, "DxcCreateInstance failed");
|
||||
}
|
||||
_compiler = compiler;
|
||||
_library = library;
|
||||
_containerReflection = containerReflection;
|
||||
static bool PrintVersion = true;
|
||||
if (PrintVersion)
|
||||
{
|
||||
PrintVersion = false;
|
||||
IDxcVersionInfo* version = nullptr;
|
||||
if (compiler && SUCCEEDED(compiler->QueryInterface(__uuidof(version), reinterpret_cast<void**>(&version))))
|
||||
{
|
||||
UINT32 major, minor;
|
||||
version->GetVersion(&major, &minor);
|
||||
LOG(Info, "DXC version {0}.{1}", major, minor);
|
||||
version->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ShaderCompilerDX::~ShaderCompilerDX()
|
||||
{
|
||||
auto compiler = (IDxcCompiler2*)_compiler;
|
||||
if (compiler)
|
||||
compiler->Release();
|
||||
auto library = (IDxcLibrary*)_library;
|
||||
if (library)
|
||||
library->Release();
|
||||
auto containerReflection = (IDxcContainerReflection*)_containerReflection;
|
||||
if (containerReflection)
|
||||
containerReflection->Release();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ProcessShader(ShaderCompilationContext* context, Array<ShaderCompiler::ShaderResourceBuffer>& constantBuffers, ID3D12ShaderReflection* shaderReflection, D3D12_SHADER_DESC& desc, ShaderBindings& bindings)
|
||||
{
|
||||
// Extract constant buffers usage information
|
||||
for (uint32 a = 0; a < desc.ConstantBuffers; a++)
|
||||
{
|
||||
// Get CB
|
||||
auto cb = shaderReflection->GetConstantBufferByIndex(a);
|
||||
|
||||
// Get CB description
|
||||
D3D12_SHADER_BUFFER_DESC cbDesc;
|
||||
cb->GetDesc(&cbDesc);
|
||||
|
||||
// Check buffer type
|
||||
if (cbDesc.Type == D3D_CT_CBUFFER)
|
||||
{
|
||||
// Find CB slot index
|
||||
int32 slot = INVALID_INDEX;
|
||||
for (uint32 b = 0; b < desc.BoundResources; b++)
|
||||
{
|
||||
D3D12_SHADER_INPUT_BIND_DESC bDesc;
|
||||
shaderReflection->GetResourceBindingDesc(b, &bDesc);
|
||||
if (StringUtils::Compare(bDesc.Name, cbDesc.Name) == 0)
|
||||
{
|
||||
slot = bDesc.BindPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot == INVALID_INDEX)
|
||||
{
|
||||
context->OnError("Missing bound resource.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set flag
|
||||
bindings.UsedCBsMask |= 1 << slot;
|
||||
|
||||
// Try to add CB to the list
|
||||
for (int32 b = 0; b < constantBuffers.Count(); b++)
|
||||
{
|
||||
auto& cc = constantBuffers[b];
|
||||
if (cc.Slot == slot)
|
||||
{
|
||||
cc.IsUsed = true;
|
||||
cc.Size = cbDesc.Size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract resources usage
|
||||
for (uint32 i = 0; i < desc.BoundResources; i++)
|
||||
{
|
||||
// Get resource description
|
||||
D3D12_SHADER_INPUT_BIND_DESC resDesc;
|
||||
shaderReflection->GetResourceBindingDesc(i, &resDesc);
|
||||
|
||||
switch (resDesc.Type)
|
||||
{
|
||||
// Sampler
|
||||
case D3D_SIT_SAMPLER:
|
||||
break;
|
||||
|
||||
// Constant Buffer
|
||||
case D3D_SIT_CBUFFER:
|
||||
case D3D_SIT_TBUFFER:
|
||||
break;
|
||||
|
||||
// Shader Resource
|
||||
case D3D_SIT_TEXTURE:
|
||||
case D3D_SIT_STRUCTURED:
|
||||
case D3D_SIT_BYTEADDRESS:
|
||||
bindings.UsedSRsMask |= 1 << resDesc.BindPoint;
|
||||
break;
|
||||
|
||||
// Unordered Access
|
||||
case D3D_SIT_UAV_RWTYPED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED:
|
||||
case D3D_SIT_UAV_RWBYTEADDRESS:
|
||||
case D3D_SIT_UAV_APPEND_STRUCTURED:
|
||||
case D3D_SIT_UAV_CONSUME_STRUCTURED:
|
||||
case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER:
|
||||
bindings.UsedUAsMask |= 1 << resDesc.BindPoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite)
|
||||
{
|
||||
if (WriteShaderFunctionBegin(_context, meta))
|
||||
return true;
|
||||
|
||||
// Prepare
|
||||
auto options = _context->Options;
|
||||
auto compiler = (IDxcCompiler2*)_compiler;
|
||||
auto library = (IDxcLibrary*)_library;
|
||||
auto containerReflection = (IDxcContainerReflection*)_containerReflection;
|
||||
auto type = meta.GetStage();
|
||||
IncludeDX include(_context, library);
|
||||
const Char* targetProfile;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
targetProfile = TEXT("vs_6_0");
|
||||
break;
|
||||
case ShaderStage::Hull:
|
||||
targetProfile = TEXT("hs_6_0");
|
||||
break;
|
||||
case ShaderStage::Domain:
|
||||
targetProfile = TEXT("ds_6_0");
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
targetProfile = TEXT("gs_6_0");
|
||||
break;
|
||||
case ShaderStage::Pixel:
|
||||
targetProfile = TEXT("ps_6_0");
|
||||
break;
|
||||
case ShaderStage::Compute:
|
||||
targetProfile = TEXT("cs_6_0");
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
ComPtr<IDxcBlobEncoding> textBlob;
|
||||
if (FAILED(library->CreateBlobWithEncodingFromPinned((LPBYTE)options->Source, options->SourceLength, CP_UTF8, &textBlob)))
|
||||
return true;
|
||||
const StringAsUTF16<> entryPoint(meta.Name.Get(), meta.Name.Length());
|
||||
Array<String> definesStrings;
|
||||
Array<DxcDefine> defines;
|
||||
Array<Char*, FixedAllocation<16>> args;
|
||||
if (_context->Options->NoOptimize)
|
||||
args.Add(TEXT("-Od"));
|
||||
else
|
||||
args.Add(TEXT("-O3"));
|
||||
if (_context->Options->TreatWarningsAsErrors)
|
||||
args.Add(TEXT("-WX"));
|
||||
if (_context->Options->GenerateDebugData)
|
||||
args.Add(TEXT("-Zi"));
|
||||
|
||||
// Compile all shader function permutations
|
||||
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
|
||||
{
|
||||
_macros.Clear();
|
||||
|
||||
// Get function permutation macros
|
||||
meta.GetDefinitionsForPermutation(permutationIndex, _macros);
|
||||
|
||||
// Add additional define for compiled function name
|
||||
GetDefineForFunction(meta, _macros);
|
||||
|
||||
// Add custom and global macros (global last because contain null define to indicate ending)
|
||||
_macros.Add(_context->Options->Macros);
|
||||
_macros.Add(_globalMacros);
|
||||
|
||||
// Convert defines from char* to Char*
|
||||
const int32 macrosCount = _macros.Count() - 1;
|
||||
definesStrings.Resize(macrosCount * 2);
|
||||
defines.Resize(macrosCount);
|
||||
for (int32 i = 0; i < macrosCount; i++)
|
||||
{
|
||||
auto& macro = _macros[i];
|
||||
auto& define = defines[i];
|
||||
auto& defineName = definesStrings[i * 2];
|
||||
auto& defineValue = definesStrings[i * 2 + 1];
|
||||
defineName = macro.Name;
|
||||
defineValue = macro.Definition;
|
||||
define.Name = defineName.GetText();
|
||||
define.Value = defineValue.Get();
|
||||
}
|
||||
|
||||
// Compile
|
||||
ComPtr<IDxcOperationResult> results;
|
||||
HRESULT result = compiler->Compile(
|
||||
textBlob.Get(),
|
||||
options->TargetName.Get(),
|
||||
entryPoint.Get(),
|
||||
targetProfile,
|
||||
(LPCWSTR*)args.Get(),
|
||||
args.Count(),
|
||||
defines.Get(),
|
||||
defines.Count(),
|
||||
&include,
|
||||
&results);
|
||||
if (SUCCEEDED(result) && results)
|
||||
results->GetStatus(&result);
|
||||
if (FAILED(result))
|
||||
{
|
||||
if (results)
|
||||
{
|
||||
ComPtr<IDxcBlobEncoding> error;
|
||||
results->GetErrorBuffer(&error);
|
||||
if (error && error->GetBufferSize() > 0)
|
||||
{
|
||||
ComPtr<IDxcBlobEncoding> errorUtf8;
|
||||
library->GetBlobAsUtf8(error, &errorUtf8);
|
||||
if (errorUtf8)
|
||||
{
|
||||
_context->OnError((const char*)errorUtf8->GetBufferPointer());
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the output
|
||||
ComPtr<IDxcBlob> shaderBuffer = nullptr;
|
||||
if (FAILED(results->GetResult(&shaderBuffer)))
|
||||
{
|
||||
LOG(Error, "IDxcOperationResult::GetResult failed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef GPU_USE_SHADERS_DEBUG_LAYER
|
||||
// Generate debug information
|
||||
{
|
||||
// Disassemble compiled shader
|
||||
ComPtr<IDxcBlobEncoding> disassembly;
|
||||
if (FAILED(compiler->Disassemble(shaderBuffer, &disassembly)))
|
||||
return true;
|
||||
ComPtr<IDxcBlobEncoding> disassemblyUtf8;
|
||||
if (FAILED(library->GetBlobAsUtf8(disassembly, &disassemblyUtf8)))
|
||||
return true;
|
||||
|
||||
// Extract debug info
|
||||
_context->OnCollectDebugInfo(meta, permutationIndex, (const char*)disassemblyUtf8->GetBufferPointer(), (int32)disassemblyUtf8->GetBufferSize());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Perform shader reflection
|
||||
if (FAILED(containerReflection->Load(shaderBuffer)))
|
||||
{
|
||||
LOG(Error, "IDxcContainerReflection::Load failed.");
|
||||
return true;
|
||||
}
|
||||
const uint32 dxilPartKind = DXIL_FOURCC('D', 'X', 'I', 'L');
|
||||
uint32 dxilPartIndex = ~0u;
|
||||
if (FAILED(containerReflection->FindFirstPartKind(dxilPartKind, &dxilPartIndex)))
|
||||
{
|
||||
LOG(Error, "IDxcContainerReflection::FindFirstPartKind failed.");
|
||||
return true;
|
||||
}
|
||||
ComPtr<ID3D12ShaderReflection> shaderReflection;
|
||||
if (FAILED(containerReflection->GetPartReflection(dxilPartIndex, IID_PPV_ARGS(&shaderReflection))))
|
||||
{
|
||||
LOG(Error, "IDxcContainerReflection::GetPartReflection failed.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get shader description
|
||||
D3D12_SHADER_DESC desc;
|
||||
Platform::MemoryClear(&desc, sizeof(desc));
|
||||
shaderReflection->GetDesc(&desc);
|
||||
|
||||
// Process shader reflection data
|
||||
ShaderBindings bindings = { desc.InstructionCount, 0, 0, 0 };
|
||||
if (ProcessShader(_context, _constantBuffers, shaderReflection.Get(), desc, bindings))
|
||||
return true;
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize()))
|
||||
return true;
|
||||
|
||||
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
|
||||
return true;
|
||||
}
|
||||
|
||||
return WriteShaderFunctionEnd(_context, meta);
|
||||
}
|
||||
|
||||
bool ShaderCompilerDX::OnCompileBegin()
|
||||
{
|
||||
if (ShaderCompiler::OnCompileBegin())
|
||||
return true;
|
||||
|
||||
_globalMacros.Add({ "DIRECTX", "1" });
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
41
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h
Normal file
41
Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_DX_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/ShaderCompiler.h"
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for DirectX platform using https://github.com/microsoft/DirectXShaderCompiler.
|
||||
/// </summary>
|
||||
class ShaderCompilerDX : public ShaderCompiler
|
||||
{
|
||||
private:
|
||||
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
void* _compiler;
|
||||
void* _library;
|
||||
void* _containerReflection;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompilerDX"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompilerDX(ShaderProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ShaderCompilerDX"/> class.
|
||||
/// </summary>
|
||||
~ShaderCompilerDX();
|
||||
|
||||
protected:
|
||||
|
||||
// [ShaderCompiler]
|
||||
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
|
||||
bool OnCompileBegin() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Config.h"
|
||||
|
||||
#define XSC_ENABLE_LANGUAGE_EXT 1
|
||||
#include <Xsc/Xsc.h>
|
||||
#pragma comment(lib, "XShaderCompiler.lib")
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// OpenGL shaders compiler module.
|
||||
/// </summary>
|
||||
public class ShaderCompilerOGL : ShaderCompiler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_OGL_SHADER_COMPILER");
|
||||
}
|
||||
}
|
||||
585
Source/Engine/ShadersCompilation/OpenGL/ShaderCompilerOGL.cpp
Normal file
585
Source/Engine/ShadersCompilation/OpenGL/ShaderCompilerOGL.cpp
Normal file
@@ -0,0 +1,585 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_OGL_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilerOGL.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "../RenderToolsOGL.h"
|
||||
#include "../ShaderAPI.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <LZ4/lz4.h>
|
||||
|
||||
ShaderCompilerOGL::ShaderCompilerOGL(ShaderProfile profile)
|
||||
: _profile(profile)
|
||||
, _context(nullptr)
|
||||
, _sourceStream(0)
|
||||
{
|
||||
// Check for supported profiles
|
||||
ASSERT(profile == ShaderProfile::GLSL_410 || profile == ShaderProfile::GLSL_440);
|
||||
}
|
||||
|
||||
ShaderCompilerOGL::~ShaderCompilerOGL()
|
||||
{
|
||||
}
|
||||
|
||||
#define USE_DETAILED_LOG 1
|
||||
|
||||
class XscLog : public Xsc::Log
|
||||
{
|
||||
public:
|
||||
|
||||
std::stringstream Log;
|
||||
|
||||
private:
|
||||
|
||||
#if USE_DETAILED_LOG
|
||||
|
||||
static void PrintMultiLineString(std::stringstream& output, const std::string& str, const std::string& indent)
|
||||
{
|
||||
// Determine at which position the actual text begins (excluding the "error (X:Y) : " or the like)
|
||||
auto textStartPos = str.find(" : ");
|
||||
if (textStartPos != std::string::npos)
|
||||
textStartPos += 3;
|
||||
else
|
||||
textStartPos = 0;
|
||||
|
||||
std::string newLineIndent(textStartPos, ' ');
|
||||
|
||||
size_t start = 0;
|
||||
bool useNewLineIndent = false;
|
||||
while (start < str.size())
|
||||
{
|
||||
output << indent;
|
||||
|
||||
if (useNewLineIndent)
|
||||
output << newLineIndent;
|
||||
|
||||
// Print next line
|
||||
auto end = str.find('\n', start);
|
||||
|
||||
if (end != std::string::npos)
|
||||
{
|
||||
output << str.substr(start, end - start);
|
||||
start = end + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
output << str.substr(start);
|
||||
start = end;
|
||||
}
|
||||
|
||||
output << std::endl;
|
||||
useNewLineIndent = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintReport(std::stringstream& output, const Xsc::Report& report, const std::string& indent)
|
||||
{
|
||||
// Print optional context description
|
||||
auto context = report.Context();
|
||||
if (!context.empty())
|
||||
PrintMultiLineString(output, context, indent);
|
||||
|
||||
// Print report message
|
||||
auto msg = report.Message();
|
||||
PrintMultiLineString(output, msg, indent);
|
||||
|
||||
// Print optional line and line-marker
|
||||
if (report.HasLine())
|
||||
{
|
||||
const auto& line = report.Line();
|
||||
const auto& marker = report.Marker();
|
||||
|
||||
// Print line
|
||||
output << indent << line << std::endl;
|
||||
|
||||
// Print line marker
|
||||
output << indent << marker << std::endl;
|
||||
}
|
||||
|
||||
// Print optional hints
|
||||
auto hints = report.GetHints();
|
||||
if (hints.size() > 0)
|
||||
{
|
||||
for (auto hint : hints)
|
||||
output << indent << hint << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
// [Log]
|
||||
void SubmitReport(const Xsc::Report& report) override
|
||||
{
|
||||
#if !USE_DETAILED_LOG
|
||||
std::string str = report.Message();
|
||||
switch (report.Type())
|
||||
{
|
||||
case Xsc::ReportTypes::Info:
|
||||
Log << "Info: " << str << std::endl;
|
||||
break;
|
||||
case Xsc::ReportTypes::Warning:
|
||||
Log << "Warning: " << str << std::endl;
|
||||
break;
|
||||
case Xsc::ReportTypes::Error:
|
||||
Log << "Error: " << str << std::endl;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (report.Type())
|
||||
{
|
||||
case Xsc::ReportTypes::Info:
|
||||
Log << "Info: " << std::endl;
|
||||
break;
|
||||
case Xsc::ReportTypes::Warning:
|
||||
Log << "Warning: " << std::endl;
|
||||
break;
|
||||
case Xsc::ReportTypes::Error:
|
||||
Log << "Error: " << std::endl;
|
||||
break;
|
||||
}
|
||||
PrintReport(Log, report, FullIndent());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
int GetUniformSize(Xsc::Reflection::DataType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
//case Xsc::Reflection::DataType::Undefined:
|
||||
|
||||
// String types:
|
||||
//case Xsc::Reflection::DataType::String:
|
||||
|
||||
// Scalar types
|
||||
case Xsc::Reflection::DataType::Bool: return 1;
|
||||
case Xsc::Reflection::DataType::Int: return 4;
|
||||
case Xsc::Reflection::DataType::UInt: return 4;
|
||||
case Xsc::Reflection::DataType::Half: return 2;
|
||||
case Xsc::Reflection::DataType::Float: return 4;
|
||||
case Xsc::Reflection::DataType::Double: return 8;
|
||||
|
||||
// Vector types
|
||||
case Xsc::Reflection::DataType::Bool2: return 1 * 2;
|
||||
case Xsc::Reflection::DataType::Bool3: return 1 * 3;
|
||||
case Xsc::Reflection::DataType::Bool4: return 1 * 4;
|
||||
case Xsc::Reflection::DataType::Int2: return 4 * 2;
|
||||
case Xsc::Reflection::DataType::Int3: return 4 * 3;
|
||||
case Xsc::Reflection::DataType::Int4: return 4 * 4;
|
||||
case Xsc::Reflection::DataType::UInt2: return 4 * 2;
|
||||
case Xsc::Reflection::DataType::UInt3: return 4 * 3;
|
||||
case Xsc::Reflection::DataType::UInt4: return 4 * 4;
|
||||
case Xsc::Reflection::DataType::Half2: return 2 * 2;
|
||||
case Xsc::Reflection::DataType::Half3: return 2 * 3;
|
||||
case Xsc::Reflection::DataType::Half4: return 2 * 4;
|
||||
case Xsc::Reflection::DataType::Float2: return 4 * 2;
|
||||
case Xsc::Reflection::DataType::Float3: return 4 * 3;
|
||||
case Xsc::Reflection::DataType::Float4: return 4 * 4;
|
||||
case Xsc::Reflection::DataType::Double2: return 8 * 2;
|
||||
case Xsc::Reflection::DataType::Double3: return 8 * 3;
|
||||
case Xsc::Reflection::DataType::Double4: return 8 * 4;
|
||||
|
||||
// Matrix types
|
||||
case Xsc::Reflection::DataType::Bool2x2: return 1 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::Bool2x3: return 1 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::Bool2x4: return 1 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::Bool3x2: return 1 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::Bool3x3: return 1 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::Bool3x4: return 1 * 3 * 5;
|
||||
case Xsc::Reflection::DataType::Bool4x2: return 1 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::Bool4x3: return 1 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::Bool4x4: return 1 * 4 * 4;
|
||||
case Xsc::Reflection::DataType::Int2x2: return 4 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::Int2x3: return 4 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::Int2x4: return 4 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::Int3x2: return 4 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::Int3x3: return 4 * 3 * 3;
|
||||
case Xsc::Reflection::DataType::Int3x4: return 4 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::Int4x2: return 4 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::Int4x3: return 4 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::Int4x4: return 4 * 4 * 4;
|
||||
case Xsc::Reflection::DataType::UInt2x2: return 4 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::UInt2x3: return 4 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::UInt2x4: return 4 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::UInt3x2: return 4 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::UInt3x3: return 4 * 3 * 3;
|
||||
case Xsc::Reflection::DataType::UInt3x4: return 4 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::UInt4x2: return 4 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::UInt4x3: return 4 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::UInt4x4: return 4 * 4 * 4;
|
||||
case Xsc::Reflection::DataType::Half2x2: return 2 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::Half2x3: return 2 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::Half2x4: return 2 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::Half3x2: return 2 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::Half3x3: return 2 * 3 * 3;
|
||||
case Xsc::Reflection::DataType::Half3x4: return 2 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::Half4x2: return 2 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::Half4x3: return 2 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::Half4x4: return 2 * 4 * 4;
|
||||
case Xsc::Reflection::DataType::Float2x2: return 4 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::Float2x3: return 4 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::Float2x4: return 4 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::Float3x2: return 4 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::Float3x3: return 4 * 3 * 3;
|
||||
case Xsc::Reflection::DataType::Float3x4: return 4 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::Float4x2: return 4 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::Float4x3: return 4 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::Float4x4: return 4 * 4 * 4;
|
||||
case Xsc::Reflection::DataType::Double2x2: return 8 * 2 * 2;
|
||||
case Xsc::Reflection::DataType::Double2x3: return 8 * 2 * 3;
|
||||
case Xsc::Reflection::DataType::Double2x4: return 8 * 2 * 4;
|
||||
case Xsc::Reflection::DataType::Double3x2: return 8 * 3 * 2;
|
||||
case Xsc::Reflection::DataType::Double3x3: return 8 * 3 * 3;
|
||||
case Xsc::Reflection::DataType::Double3x4: return 8 * 3 * 4;
|
||||
case Xsc::Reflection::DataType::Double4x2: return 8 * 4 * 2;
|
||||
case Xsc::Reflection::DataType::Double4x3: return 8 * 4 * 3;
|
||||
case Xsc::Reflection::DataType::Double4x4: return 8 * 4 * 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ShaderCompilerOGL::ProcessShader(Xsc::Reflection::ReflectionData& data, uint32& cbMask)
|
||||
{
|
||||
// Reset masks
|
||||
cbMask = 0;
|
||||
srMask = 0;
|
||||
uaMask = 0;
|
||||
|
||||
// Extract constant buffers usage information
|
||||
for (const auto& cb : data.constantBuffers)
|
||||
{
|
||||
if (cb.location < 0)
|
||||
{
|
||||
_context->OnError("Missing bound resource.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set flag
|
||||
cbMask |= 1 << cb.location;
|
||||
|
||||
// Try to add CB to the list
|
||||
for (int32 b = 0; b < _constantBuffers.Count(); b++)
|
||||
{
|
||||
auto& cc = _constantBuffers[b];
|
||||
if (cc.Slot == cb.location)
|
||||
{
|
||||
// Calculate the size (based on uniforms used in this buffer)
|
||||
cc.Size = 0;
|
||||
for (const auto& uniform : data.uniforms)
|
||||
{
|
||||
if (uniform.type == Xsc::Reflection::UniformType::Variable
|
||||
&& uniform.uniformBlock == cb.location)
|
||||
{
|
||||
cc.Size += GetUniformSize((Xsc::Reflection::DataType)uniform.baseType);
|
||||
}
|
||||
}
|
||||
|
||||
cc.IsUsed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract resources usage
|
||||
for (const auto& sr : data.textures)
|
||||
{
|
||||
if (sr.location < 0)
|
||||
{
|
||||
_context->OnError("Missing bound resource.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set flag
|
||||
srMask |= 1 << sr.location;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompilerOGL::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite)
|
||||
{
|
||||
// Prepare
|
||||
auto output = _context->Output;
|
||||
auto options = _context->Options;
|
||||
auto type = meta.GetStage();
|
||||
auto permutationsCount = meta.GetPermutationsCount();
|
||||
|
||||
// Construct shader target
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex: _inputDesc.shaderTarget = Xsc::ShaderTarget::VertexShader; break;
|
||||
case ShaderStage::Hull: _inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationControlShader; break;
|
||||
case ShaderStage::Domain: _inputDesc.shaderTarget = Xsc::ShaderTarget::TessellationEvaluationShader; break;
|
||||
case ShaderStage::Geometry: _inputDesc.shaderTarget = Xsc::ShaderTarget::GeometryShader; break;
|
||||
case ShaderStage::Pixel: _inputDesc.shaderTarget = Xsc::ShaderTarget::FragmentShader; break;
|
||||
case ShaderStage::Compute: _inputDesc.shaderTarget = Xsc::ShaderTarget::ComputeShader; break;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
// [Output] Type
|
||||
output->WriteByte(static_cast<byte>(type));
|
||||
|
||||
// [Output] Permutations count
|
||||
output->WriteByte(permutationsCount);
|
||||
|
||||
// [Output] Shader function name
|
||||
output->WriteStringAnsi(meta.Name, 11);
|
||||
|
||||
// Compile all shader function permutations
|
||||
for (int32 permutationIndex = 0; permutationIndex < permutationsCount; permutationIndex++)
|
||||
{
|
||||
_macros.Clear();
|
||||
|
||||
// Get function permutation macros
|
||||
meta.GetDefinitionsForPermutation(permutationIndex, _macros);
|
||||
|
||||
// Add additional define for compiled function name
|
||||
GetDefineForFunction(meta, _macros);
|
||||
|
||||
// Add custom and global macros (global last because contain null define to indicate ending)
|
||||
_macros.Add(_context->Options->Macros);
|
||||
_macros.Add(_globalMacros);
|
||||
|
||||
// Setup the source code
|
||||
// TODO: use shared _sourceStream and reduce dynamic memory allocations
|
||||
std::shared_ptr<std::stringstream> input = std::shared_ptr<std::stringstream>(new std::stringstream());
|
||||
for (int32 i = 0; i < _macros.Count(); i++)
|
||||
{
|
||||
ASSERT(_macros[i].Name && _macros[i].Definition);
|
||||
*input << "#define " << _macros[i].Name << " " << _macros[i].Definition << "\n";
|
||||
}
|
||||
*input << options->Source;
|
||||
|
||||
// Setup options for this permutation
|
||||
std::stringstream outputStream;// TODO: reuse it to reduce dynamic memory allocations
|
||||
_inputDesc.sourceCode = input;
|
||||
_inputDesc.entryPoint = meta.Name;
|
||||
_outputDesc.sourceCode = &outputStream;
|
||||
|
||||
// Compile
|
||||
Xsc::Reflection::ReflectionData reflection;
|
||||
try
|
||||
{
|
||||
// Captures and prints to log the full AST tree
|
||||
#define DEBUG_AST 0
|
||||
#if DEBUG_AST
|
||||
_outputDesc.options.showAST = true;
|
||||
std::stringstream buffer;
|
||||
std::streambuf* old = std::cout.rdbuf(buffer.rdbuf());
|
||||
#endif
|
||||
|
||||
// Cross-compile HLSL to GLSL
|
||||
XscLog log;
|
||||
bool result = Xsc::CompileShader(_inputDesc, _outputDesc, &log, &reflection);
|
||||
|
||||
#if DEBUG_AST
|
||||
String text = String(buffer.str());
|
||||
LOG_STR(Info, text);
|
||||
#endif
|
||||
|
||||
// Check compilation result
|
||||
if (!result)
|
||||
{
|
||||
auto str = log.Log.str();
|
||||
_context->OnError(str.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
_context->OnError(e.what());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process reflection data
|
||||
uint32 cbMask, uaMsrMaskask, uaMask;
|
||||
if (ProcessShader(reflection, cbMask, srMaskk, uaMask))
|
||||
return true;
|
||||
|
||||
// Get the source code (and append the null-terminator)
|
||||
auto str = outputStream.str();
|
||||
str += "\0";
|
||||
|
||||
// Prepare the shader buffer (for raw mode)
|
||||
int32 shaderBufferType = SHADER_DATA_FORMAT_RAW;
|
||||
uint32 shaderBufferSize = (uint32)str.length() + 1;
|
||||
uint32 shaderBufferOriginalSize = shaderBufferSize;
|
||||
byte* shaderBuffer = (byte*)str.c_str();
|
||||
|
||||
// Try to compress the source code (don't compress if memory gain is too small)
|
||||
{
|
||||
float maxCompressionRatio = 0.75f;
|
||||
|
||||
// Try use LZ4
|
||||
int32 srcSize = (int32)shaderBufferSize;
|
||||
int32 estSize = LZ4_compressBound(srcSize);
|
||||
_dataCompressedCache.Clear();
|
||||
_dataCompressedCache.EnsureCapacity(estSize);
|
||||
int32 dstSize = LZ4_compress_default(str.c_str(), (char*)_dataCompressedCache.Get(), srcSize, estSize);
|
||||
float ratio = (float)dstSize / srcSize;
|
||||
if (dstSize == 0)
|
||||
{
|
||||
LOG(Warning, "Shader source data LZ4 compression failed.");
|
||||
}
|
||||
else if (ratio <= maxCompressionRatio)
|
||||
{
|
||||
// Use compressed format
|
||||
shaderBufferType = SHADER_DATA_FORMAT_LZ4;
|
||||
shaderBufferSize = dstSize;
|
||||
shaderBuffer = _dataCompressedCache.Get();
|
||||
}
|
||||
}
|
||||
|
||||
// [Output] Write cross-compiled shader function
|
||||
output->WriteInt32(shaderBufferType);
|
||||
output->WriteUint32(shaderBufferOriginalSize);
|
||||
output->WriteUint32(shaderBufferSize);
|
||||
output->WriteBytes(shaderBuffer, shaderBufferSize);
|
||||
|
||||
// [Output] Shader meta
|
||||
uint32 instructionCount = 0;// TODO: use AST reflection to count the shader instructions
|
||||
output->WriteUint32(instructionCount);
|
||||
output->WriteUint32(cbMask);
|
||||
output->WriteUint32(srMask);
|
||||
output->WriteUint32(uaMask);
|
||||
|
||||
// Custom data
|
||||
if (customDataWrite && customDataWrite(output, meta, permutationIndex, _macros))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class FlaxIncludeHandler : public Xsc::IncludeHandler
|
||||
{
|
||||
public:
|
||||
|
||||
FlaxIncludeHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<std::istream> Include(const std::string& includeName, bool useSearchPathsFirst) override
|
||||
{
|
||||
const String filename(includeName);
|
||||
auto shaderApi = ShaderAPI::Instance();
|
||||
|
||||
ScopeLock lock(shaderApi->Locker);
|
||||
|
||||
const auto shader = shaderApi->GetShaderSource(filename);
|
||||
if (shader == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<std::stringstream> stream = std::unique_ptr<std::stringstream>(new std::stringstream());
|
||||
*stream << shader->Get();
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
bool ShaderCompilerOGL::Compile(ShaderCompilationContext* context)
|
||||
{
|
||||
// Clear cache
|
||||
_globalMacros.Clear();
|
||||
_macros.Clear();
|
||||
_constantBuffers.Clear();
|
||||
_context = context;
|
||||
|
||||
// Prepare
|
||||
auto options = context->Options;
|
||||
auto output = context->Output;
|
||||
auto meta = context->Meta;
|
||||
bool use440 = _profile == ShaderProfile::GLSL_440;
|
||||
int32 shadersCount = meta->GetShadersCount();
|
||||
FlaxIncludeHandler includeHandler;
|
||||
|
||||
// Setup global shader macros
|
||||
_globalMacros.EnsureCapacity(32);
|
||||
_macros.EnsureCapacity(32);
|
||||
_globalMacros.Add({ "OPENGL", "1" });
|
||||
GetGlobalDefines(_globalMacros);
|
||||
|
||||
// Allocate shader source stream data (it will include compilation macros)
|
||||
//uint32 macrosCountApprox = _globalMacros.Count() + options->Macros.Count() + 10;
|
||||
//_sourceStream.Reset(Math::Max<uint32>(_sourceStream.GetCapacity(), sizeof(Char) * Math::RoundUpToPowerOf2<uint32>(options->SourceLength + 32 * macrosCountApprox)));
|
||||
// TODO: preallocate _sourceStream to reduce dynamic allocations
|
||||
|
||||
// Setup cross-compiler options
|
||||
{
|
||||
switch (_profile)
|
||||
{
|
||||
case ShaderProfile::GLSL_440: _inputDesc.shaderVersion = Xsc::InputShaderVersion::HLSL5; break;
|
||||
case ShaderProfile::GLSL_410: _inputDesc.shaderVersion = Xsc::InputShaderVersion::HLSL4; break;
|
||||
}
|
||||
_inputDesc.filename = context->TargetNameAnsi;
|
||||
_inputDesc.extensions = Xsc::Extensions::LayoutAttribute;
|
||||
_inputDesc.includeHandler = &includeHandler;
|
||||
}
|
||||
{
|
||||
switch (_profile)
|
||||
{
|
||||
case ShaderProfile::GLSL_440: _outputDesc.shaderVersion = Xsc::OutputShaderVersion::GLSL440; break;
|
||||
case ShaderProfile::GLSL_410: _outputDesc.shaderVersion = Xsc::OutputShaderVersion::GLSL410; break;
|
||||
}
|
||||
_outputDesc.options.optimize = !options->NoOptimize;
|
||||
_outputDesc.options.separateShaders = true;
|
||||
_outputDesc.options.separateSamplers = true;
|
||||
_outputDesc.options.preserveComments = false;
|
||||
_outputDesc.options.explicitBinding = true;
|
||||
_outputDesc.formatting.writeGeneratorHeader = false;
|
||||
_outputDesc.formatting.blanks = false;
|
||||
_outputDesc.nameMangling.inputPrefix = "f_";
|
||||
_outputDesc.nameMangling.outputPrefix = "f_";
|
||||
_outputDesc.nameMangling.useAlwaysSemantics = true;
|
||||
_outputDesc.nameMangling.renameBufferFields = true;
|
||||
}
|
||||
|
||||
// Setup constant buffers cache
|
||||
_constantBuffers.EnsureCapacity(meta->CB.Count(), false);
|
||||
for (int32 i = 0; i < meta->CB.Count(); i++)
|
||||
_constantBuffers.Add({ meta->CB[i].Slot, false, 0 });
|
||||
|
||||
// [Output] Write version number
|
||||
output->WriteInt32(1);
|
||||
|
||||
// [Output] Write amount of shaders
|
||||
output->WriteInt32(shadersCount);
|
||||
|
||||
// Compile all shaders
|
||||
if (CompileShaders(context))
|
||||
return true;
|
||||
|
||||
// [Output] Constant Buffers
|
||||
{
|
||||
int32 cbsCount = _constantBuffers.Count();
|
||||
ASSERT(cbsCount == meta->CB.Count());
|
||||
|
||||
// Find maximum used slot index
|
||||
byte maxCbSlot = 0;
|
||||
for (int32 i = 0; i < cbsCount; i++)
|
||||
{
|
||||
maxCbSlot = Math::Max(maxCbSlot, _constantBuffers[i].Slot);
|
||||
}
|
||||
|
||||
output->WriteByte(static_cast<byte>(cbsCount));
|
||||
output->WriteByte(maxCbSlot);
|
||||
// TODO: do we still need to serialize max cb slot?
|
||||
|
||||
for (int32 i = 0; i < cbsCount; i++)
|
||||
{
|
||||
output->WriteByte(_constantBuffers[i].Slot);
|
||||
output->WriteUint32(_constantBuffers[i].Size);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
50
Source/Engine/ShadersCompilation/OpenGL/ShaderCompilerOGL.h
Normal file
50
Source/Engine/ShadersCompilation/OpenGL/ShaderCompilerOGL.h
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_OGL_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/ShaderCompiler.h"
|
||||
#include "IncludeXShaderCompiler.h"
|
||||
#include "../IncludeOpenGLHeaders.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include <sstream>
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for OpenGL and OpenGL ES platforms
|
||||
/// </summary>
|
||||
class ShaderCompilerOGL : public ShaderCompiler
|
||||
{
|
||||
private:
|
||||
|
||||
ShaderProfile _profile;
|
||||
|
||||
ShaderCompilationContext* _context;
|
||||
Array<ShaderMacro> _globalMacros;
|
||||
Array<ShaderMacro> _macros;
|
||||
Array<ShaderResourceBuffer> _constantBuffers;
|
||||
Array<byte> _dataCompressedCache;
|
||||
Xsc::ShaderInput _inputDesc;
|
||||
Xsc::ShaderOutput _outputDesc;
|
||||
std::stringstream _sourceStream;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompilerOGL"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompilerOGL(ShaderProfile profile);
|
||||
|
||||
private:
|
||||
|
||||
bool ProcessShader(Xsc::Reflection::ReflectionData& data, uint32& cbMask, uint32& srMask, uint32& uaMask)
|
||||
|
||||
protected:
|
||||
|
||||
// [ShaderCompiler]
|
||||
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
|
||||
bool OnCompileBegin() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
21
Source/Engine/ShadersCompilation/Parser/Config.h
Normal file
21
Source/Engine/ShadersCompilation/Parser/Config.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Config.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Utilities/TextProcessing.h"
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
typedef TextProcessing Reader;
|
||||
typedef Reader::Token Token;
|
||||
typedef Reader::SeparatorData Separator;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Don't count ending '\0' character
|
||||
#define MACRO_LENGTH(macro) (ARRAY_COUNT(macro) - 1)
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ITokenReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for shader functions readers like Pixel Shader readers or Constant Buffer readers
|
||||
/// </summary>
|
||||
class IShaderFunctionReader : public ITokenReader
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~IShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Collects shader function reader results to the final Shader Meta
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="result">Parsing result</param>
|
||||
virtual void CollectResults(IShaderParser* parser, ShaderMeta* result) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
90
Source/Engine/ShadersCompilation/Parser/IShaderParser.h
Normal file
90
Source/Engine/ShadersCompilation/Parser/IShaderParser.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
struct ParserMacros
|
||||
{
|
||||
const Array<ShaderMacro>* Data;
|
||||
|
||||
ParserMacros(const Array<ShaderMacro>& data)
|
||||
{
|
||||
Data = &data;
|
||||
}
|
||||
|
||||
Token GetValue(Token& token) const
|
||||
{
|
||||
for (int32 i = 0; i < Data->Count(); i++)
|
||||
{
|
||||
if (token == Data->At(i).Name)
|
||||
{
|
||||
// Use macro value
|
||||
return Token(Data->At(i).Definition);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to token
|
||||
return token;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Interface describing shader source code parser
|
||||
/// </summary>
|
||||
class IShaderParser
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~IShaderParser()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parser feature level of the target platform graphics backend.
|
||||
/// </summary>
|
||||
/// <returns>The graphics feature level</returns>
|
||||
virtual FeatureLevel GetFeatureLevel() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parser macros.
|
||||
/// </summary>
|
||||
/// <returns>The macros</returns>
|
||||
virtual ParserMacros GetMacros() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets value indicating that shader processing operation failed
|
||||
/// </summary>
|
||||
/// <returns>True if shader processing failed, otherwise false</returns>
|
||||
virtual bool Failed() const = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets source code reader
|
||||
/// </summary>
|
||||
/// <returns>Source code reader</returns>
|
||||
virtual Reader& GetReader() = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Event send to the parser on reading shader source code error
|
||||
/// </summary>
|
||||
/// <param name="message">Message to send</param>
|
||||
virtual void OnError(const String& message) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Event send to the parser on reading shader source code warning
|
||||
/// </summary>
|
||||
/// <param name="message">Message to send</param>
|
||||
virtual void OnWarning(const String& message) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
44
Source/Engine/ShadersCompilation/Parser/ITokenReader.h
Normal file
44
Source/Engine/ShadersCompilation/Parser/ITokenReader.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "IShaderParser.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that can read shader source code tokens
|
||||
/// </summary>
|
||||
class ITokenReader
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~ITokenReader()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Checks if given token can be processed by this reader
|
||||
/// </summary>
|
||||
/// <param name="token">Starting token to check</param>
|
||||
/// <returns>True if given token is valid starting token, otherwise false</returns>
|
||||
virtual bool CheckStartToken(const Token& token) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Start processing source after reading start token
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="text">Source code reader</param>
|
||||
virtual void Process(IShaderParser* parser, Reader& text) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "ITokenReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for objects that can have child token readers
|
||||
/// </summary>
|
||||
template<typename Type>
|
||||
class ITokenReadersContainerBase
|
||||
{
|
||||
protected:
|
||||
|
||||
Array<Type*> _childReaders;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~ITokenReadersContainerBase()
|
||||
{
|
||||
// Cleanup
|
||||
_childReaders.ClearDelete();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// <summary>
|
||||
/// Try to process given token by any child reader
|
||||
/// </summary>
|
||||
/// <param name="token">Starting token to check</param>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <returns>True if no token processing has been done, otherwise false</returns>
|
||||
virtual bool ProcessChildren(const Token& token, IShaderParser* parser)
|
||||
{
|
||||
for (int32 i = 0; i < _childReaders.Count(); i++)
|
||||
{
|
||||
if (_childReaders[i]->CheckStartToken(token))
|
||||
{
|
||||
_childReaders[i]->Process(parser, parser->GetReader());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Interface for objects that can have child ITokenReader objects
|
||||
/// </summary>
|
||||
class ITokenReadersContainer : public ITokenReadersContainerBase<ITokenReader>
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~ITokenReadersContainer()
|
||||
{
|
||||
// Cleanup
|
||||
_childReaders.ClearDelete();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant Buffers reader
|
||||
/// </summary>
|
||||
class ConstantBufferReader : public ShaderMetaReader<ConstantBufferMeta>
|
||||
{
|
||||
private:
|
||||
|
||||
Token _endToken;
|
||||
|
||||
DECLARE_SHADER_META_READER_HEADER("META_CB_BEGIN", CB);
|
||||
|
||||
ConstantBufferReader()
|
||||
: _endToken("META_CB_END")
|
||||
{
|
||||
}
|
||||
|
||||
~ConstantBufferReader()
|
||||
{
|
||||
}
|
||||
|
||||
// [ShaderMetaReader]
|
||||
void OnParseBefore(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
|
||||
// Clear current meta
|
||||
_current.Name.Clear();
|
||||
|
||||
// Here we read '(x)\n' where 'x' is a shader function slot
|
||||
text.ReadToken(&token);
|
||||
if (StringUtils::Parse(token.Start, token.Length, &_current.Slot))
|
||||
{
|
||||
parser->OnError(TEXT("Invalid constant buffer slot index."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Read buffer name
|
||||
text.ReadToken(&token);
|
||||
_current.Name = token.ToString();
|
||||
|
||||
// Check if name is unique
|
||||
for (int32 i = 0; i < _cache.Count(); i++)
|
||||
{
|
||||
if (_cache[i].Name == _current.Name)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Duplicated constant buffer \'{0}\'. Buffer with that name already exists."), String(_current.Name)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Read rest of the line
|
||||
text.ReadLine();
|
||||
}
|
||||
|
||||
void OnParse(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
|
||||
// Read function properties
|
||||
bool foundEnd = false;
|
||||
while (text.CanRead())
|
||||
{
|
||||
text.ReadToken(&token);
|
||||
|
||||
// Try to find the ending
|
||||
if (token == _endToken)
|
||||
{
|
||||
foundEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if end has not been found
|
||||
if (!foundEnd)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Missing constant buffer \'{0}\' ending."), String(_current.Name)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void OnParseAfter(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Cache buffer
|
||||
_cache.Add(_current);
|
||||
}
|
||||
|
||||
void CollectResults(IShaderParser* parser, ShaderMeta* result) override
|
||||
{
|
||||
// Validate constant buffer slots overlapping
|
||||
for (int32 i = 0; i < _cache.Count(); i++)
|
||||
{
|
||||
auto& first = _cache[i];
|
||||
for (int32 j = i + 1; j < _cache.Count(); j++)
|
||||
{
|
||||
auto& second = _cache[j];
|
||||
if (first.Slot == second.Slot)
|
||||
{
|
||||
parser->OnError(TEXT("Constant buffers slots are overlapping."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate amount of used constant buffers
|
||||
for (int32 i = 0; i < _cache.Count(); i++)
|
||||
{
|
||||
auto& f = _cache[i];
|
||||
if (f.Slot >= MAX_CONSTANT_BUFFER_SLOTS)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Constant buffer {0} is using invalid slot {1}. Maximum supported slot is {2}."), String(f.Name), f.Slot, MAX_CONSTANT_BUFFER_SLOTS - 1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Base
|
||||
ShaderMetaReader::CollectResults(parser, result);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute Shaders reader
|
||||
/// </summary>
|
||||
class ComputeShaderFunctionReader : public ShaderFunctionReader<ComputeShaderMeta>
|
||||
{
|
||||
DECLARE_SHADER_META_READER_HEADER("META_CS", CS);
|
||||
|
||||
ComputeShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(New<StripLineReader>("numthreads"));
|
||||
}
|
||||
|
||||
~ComputeShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Domain Shaders reader
|
||||
/// </summary>
|
||||
class DomainShaderFunctionReader : public ShaderFunctionReader<DomainShaderMeta>
|
||||
{
|
||||
DECLARE_SHADER_META_READER_HEADER("META_DS", DS);
|
||||
|
||||
DomainShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(New<StripLineReader>("domain"));
|
||||
}
|
||||
|
||||
~DomainShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Geometry Shaders reader
|
||||
/// </summary>
|
||||
class GeometryShaderFunctionReader : public ShaderFunctionReader<GeometryShaderMeta>
|
||||
{
|
||||
class MaxVertexCountReader : public ITokenReader
|
||||
{
|
||||
private:
|
||||
|
||||
Token _startToken;
|
||||
|
||||
public:
|
||||
|
||||
MaxVertexCountReader()
|
||||
: _startToken("maxvertexcount")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
return token == _startToken;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Read line to end
|
||||
text.ReadLine();
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_SHADER_META_READER_HEADER("META_GS", GS);
|
||||
|
||||
GeometryShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(New<MaxVertexCountReader>());
|
||||
}
|
||||
|
||||
~GeometryShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Hull Shaders reader
|
||||
/// </summary>
|
||||
class HullShaderFunctionReader : public ShaderFunctionReader<HullShaderMeta>
|
||||
{
|
||||
class PatchSizeReader : public ITokenReader
|
||||
{
|
||||
protected:
|
||||
|
||||
HullShaderFunctionReader* _parent;
|
||||
Token _startToken;
|
||||
|
||||
public:
|
||||
|
||||
PatchSizeReader(HullShaderFunctionReader* parent)
|
||||
: _parent(parent)
|
||||
, _startToken("META_HS_PATCH")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
return token == _startToken;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
auto& current = _parent->_current;
|
||||
|
||||
// Input control points count
|
||||
text.ReadToken(&token);
|
||||
token = parser->GetMacros().GetValue(token);
|
||||
int32 controlPointsCount;
|
||||
if (StringUtils::Parse(token.Start, token.Length, &controlPointsCount))
|
||||
{
|
||||
parser->OnError(TEXT("Cannot parse Hull shader input control points count."));
|
||||
return;
|
||||
}
|
||||
if (Math::IsNotInRange(controlPointsCount, 1, 32))
|
||||
{
|
||||
parser->OnError(TEXT("Invalid amount of control points. Valid range is [1-32]."));
|
||||
return;
|
||||
}
|
||||
current.ControlPointsCount = controlPointsCount;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_SHADER_META_READER_HEADER("META_HS", HS);
|
||||
|
||||
HullShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(New<PatchSizeReader>(this));
|
||||
_childReaders.Add(New<StripLineReader>("domain"));
|
||||
_childReaders.Add(New<StripLineReader>("partitioning"));
|
||||
_childReaders.Add(New<StripLineReader>("outputtopology"));
|
||||
_childReaders.Add(New<StripLineReader>("maxtessfactor"));
|
||||
_childReaders.Add(New<StripLineReader>("outputcontrolpoints"));
|
||||
_childReaders.Add(New<StripLineReader>("patchconstantfunc"));
|
||||
}
|
||||
|
||||
~HullShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
|
||||
void OnParseBefore(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Clear current meta
|
||||
_current.ControlPointsCount = 0;
|
||||
|
||||
// Base
|
||||
ShaderFunctionReader::OnParseBefore(parser, text);
|
||||
}
|
||||
|
||||
void OnParseAfter(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Check if errors in parsed data
|
||||
if (_current.ControlPointsCount == 0)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Hull Shader \'{0}\' has missing META_HS_PATCH macro that defines the amount of the input control points from the Input Assembler."), String(_current.Name)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Base
|
||||
ShaderFunctionReader::OnParseAfter(parser, text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Pixel Shaders reader
|
||||
/// </summary>
|
||||
class PixelShaderFunctionReader : public ShaderFunctionReader<PixelShaderMeta>
|
||||
{
|
||||
DECLARE_SHADER_META_READER_HEADER("META_PS", PS);
|
||||
|
||||
PixelShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
|
||||
~PixelShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderFunctionReader.h"
|
||||
#include "ShaderProcessing.h"
|
||||
#include "Engine/Graphics/Shaders/Config.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Vertex Shaders reader
|
||||
/// </summary>
|
||||
class VertexShaderFunctionReader : public ShaderFunctionReader<VertexShaderMeta>
|
||||
{
|
||||
class InputLayoutReader : public ITokenReader
|
||||
{
|
||||
protected:
|
||||
|
||||
VertexShaderFunctionReader* _parent;
|
||||
Token _startToken;
|
||||
|
||||
public:
|
||||
|
||||
InputLayoutReader(VertexShaderFunctionReader* parent)
|
||||
: _parent(parent)
|
||||
, _startToken("META_VS_IN_ELEMENT")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
return token == _startToken;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
VertexShaderMeta::InputElement element;
|
||||
Token token;
|
||||
auto& current = _parent->_current;
|
||||
|
||||
// Semantic type
|
||||
text.ReadToken(&token);
|
||||
element.Type = ParseInputType(token);
|
||||
|
||||
// Semantic index
|
||||
text.ReadToken(&token);
|
||||
if (StringUtils::Parse(token.Start, token.Length, &element.Index))
|
||||
{
|
||||
parser->OnError(TEXT("Cannot parse token."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Element format
|
||||
text.ReadToken(&token);
|
||||
element.Format = ParsePixelFormat(token);
|
||||
if (element.Format == PixelFormat::Unknown)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Unknown input data format \'{0}\' for the Vertex Shader."), String(token.ToString())));
|
||||
return;
|
||||
}
|
||||
|
||||
// Input slot
|
||||
text.ReadToken(&token);
|
||||
if (StringUtils::Parse(token.Start, token.Length, &element.InputSlot))
|
||||
{
|
||||
parser->OnError(TEXT("Cannot parse token."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Aligned byte offset
|
||||
text.ReadToken(&token);
|
||||
if (token == "ALIGN")
|
||||
{
|
||||
element.AlignedByteOffset = INPUT_LAYOUT_ELEMENT_ALIGN;
|
||||
}
|
||||
else if (StringUtils::Parse(token.Start, token.Length, &element.AlignedByteOffset))
|
||||
{
|
||||
parser->OnError(TEXT("Cannot parse token."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Input slot class
|
||||
text.ReadToken(&token);
|
||||
if (token == "PER_VERTEX")
|
||||
{
|
||||
element.InputSlotClass = INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA;
|
||||
}
|
||||
else if (token == "PER_INSTANCE")
|
||||
{
|
||||
element.InputSlotClass = INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Invalid input slot class type \'{0}\'."), String(token.ToString())));
|
||||
return;
|
||||
}
|
||||
|
||||
// Instance data step rate
|
||||
text.ReadToken(&token);
|
||||
if (StringUtils::Parse(token.Start, token.Length, &element.InstanceDataStepRate))
|
||||
{
|
||||
parser->OnError(TEXT("Cannot parse token."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Visible state
|
||||
text.ReadToken(&token);
|
||||
element.VisibleFlag = token.ToString();
|
||||
|
||||
current.InputLayout.Add(element);
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_SHADER_META_READER_HEADER("META_VS", VS);
|
||||
|
||||
VertexShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(New<InputLayoutReader>(this));
|
||||
}
|
||||
|
||||
~VertexShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
|
||||
void OnParseBefore(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Clear current meta
|
||||
_current.InputLayout.Clear();
|
||||
|
||||
// Base
|
||||
ShaderFunctionReader::OnParseBefore(parser, text);
|
||||
}
|
||||
|
||||
void OnParseAfter(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Check if errors in specified input layout
|
||||
if (_current.InputLayout.Count() > VERTEX_SHADER_MAX_INPUT_ELEMENTS)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Vertex Shader \'{0}\' has too many input layout elements specified. Maximum allowed amount is {1}."), String(_current.Name), VERTEX_SHADER_MAX_INPUT_ELEMENTS));
|
||||
return;
|
||||
}
|
||||
|
||||
// Base
|
||||
ShaderFunctionReader::OnParseAfter(parser, text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
446
Source/Engine/ShadersCompilation/Parser/ShaderFunctionReader.h
Normal file
446
Source/Engine/ShadersCompilation/Parser/ShaderFunctionReader.h
Normal file
@@ -0,0 +1,446 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IShaderFunctionReader.h"
|
||||
#include "ShaderMeta.h"
|
||||
#include "Config.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation of shader meta functions reader
|
||||
/// </summary>
|
||||
template<typename MetaType>
|
||||
class ShaderMetaReader : public IShaderFunctionReader, public ITokenReadersContainer
|
||||
{
|
||||
protected:
|
||||
|
||||
Array<MetaType> _cache;
|
||||
MetaType _current;
|
||||
|
||||
ShaderMetaReader()
|
||||
{
|
||||
}
|
||||
|
||||
~ShaderMetaReader()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/// <summary>
|
||||
/// Event called before parsing shader function
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="text">Source code reader</param>
|
||||
virtual void OnParseBefore(IShaderParser* parser, Reader& text) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Event called for parsing shader function
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="text">Source code reader</param>
|
||||
virtual void OnParse(IShaderParser* parser, Reader& text)
|
||||
{
|
||||
Token token;
|
||||
|
||||
// Read function properties
|
||||
while (text.CanRead())
|
||||
{
|
||||
text.ReadToken(&token);
|
||||
|
||||
// Call children
|
||||
if (ProcessChildren(token, parser))
|
||||
break;
|
||||
}
|
||||
|
||||
// Token should contain function output type, now read function name
|
||||
text.ReadToken(&token);
|
||||
_current.Name = token.ToString();
|
||||
|
||||
// Check if name is unique
|
||||
for (int32 i = 0; i < _cache.Count(); i++)
|
||||
{
|
||||
if (_cache[i].Name == _current.Name)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Duplicated function \'{0}\'. Function with that name already exists."), String(_current.Name)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event called after parsing shader function
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="text">Source code reader</param>
|
||||
virtual void OnParseAfter(IShaderParser* parser, Reader& text) = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Process final results and send them to the Shader Meta object
|
||||
/// </summary>
|
||||
/// <param name="parser">Parser object</param>
|
||||
/// <param name="result">Output shader meta</param>
|
||||
virtual void FlushCache(IShaderParser* parser, ShaderMeta* result) = 0;
|
||||
|
||||
public:
|
||||
|
||||
// [IShaderFunctionReader]
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
OnParseBefore(parser, text);
|
||||
if (parser->Failed())
|
||||
return;
|
||||
|
||||
OnParse(parser, text);
|
||||
if (parser->Failed())
|
||||
return;
|
||||
|
||||
OnParseAfter(parser, text);
|
||||
}
|
||||
|
||||
void CollectResults(IShaderParser* parser, ShaderMeta* result) override
|
||||
{
|
||||
// Validate function names
|
||||
for (int32 i = 0; i < _cache.Count(); i++)
|
||||
{
|
||||
auto& first = _cache[i];
|
||||
for (int32 j = i + 1; j < _cache.Count(); j++)
|
||||
{
|
||||
auto& second = _cache[j];
|
||||
if (first.Name == second.Name)
|
||||
{
|
||||
parser->OnError(TEXT("Duplicated shader function names."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlushCache(parser, result);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shader functions reader
|
||||
/// </summary>
|
||||
template<typename MetaType>
|
||||
class ShaderFunctionReader : public ShaderMetaReader<MetaType>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef ShaderMetaReader<MetaType> ShaderMetaReaderType;
|
||||
|
||||
protected:
|
||||
|
||||
class StripLineReader : public ITokenReader
|
||||
{
|
||||
private:
|
||||
|
||||
Token _startToken;
|
||||
|
||||
public:
|
||||
|
||||
explicit StripLineReader(const char* token)
|
||||
: _startToken(token)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
return token == _startToken;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Read line to end
|
||||
text.ReadLine();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Shader function permutations reader
|
||||
/// </summary>
|
||||
class PermutationReader : public ITokenReader
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderFunctionReader* _parent;
|
||||
int32 _startTokenPermutationSize;
|
||||
|
||||
const char* PermutationTokens[SHADER_PERMUTATIONS_MAX_PARAMS_COUNT] =
|
||||
{
|
||||
"META_PERMUTATION_1",
|
||||
"META_PERMUTATION_2",
|
||||
"META_PERMUTATION_3",
|
||||
"META_PERMUTATION_4",
|
||||
};
|
||||
static_assert(SHADER_PERMUTATIONS_MAX_PARAMS_COUNT == 4, "Invalid maximum amount of shader permutations.");
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent shader function reader object</param>
|
||||
PermutationReader(ShaderFunctionReader* parent)
|
||||
: _parent(parent)
|
||||
, _startTokenPermutationSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Clear cache
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
_startTokenPermutationSize = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
for (int32 i = 0; i < ARRAY_COUNT(PermutationTokens); i++)
|
||||
{
|
||||
if (token == PermutationTokens[i])
|
||||
{
|
||||
_startTokenPermutationSize = i + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
auto& current = _parent->_current;
|
||||
|
||||
// Add permutation
|
||||
int32 permutationIndex = current.Permutations.Count();
|
||||
auto& permutation = current.Permutations.AddOne();
|
||||
|
||||
// Read all parameters for the permutation
|
||||
ASSERT(_startTokenPermutationSize > 0);
|
||||
for (int32 paramIndex = 0; paramIndex < _startTokenPermutationSize; paramIndex++)
|
||||
{
|
||||
// Check for missing end
|
||||
if (!text.CanRead())
|
||||
{
|
||||
parser->OnError(TEXT("Missing ending of shader function permutation."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Read definition name
|
||||
text.ReadToken(&token);
|
||||
if (token.Length == 0)
|
||||
{
|
||||
parser->OnError(TEXT("Incorrect shader permutation. Definition name is empty."));
|
||||
return;
|
||||
}
|
||||
StringAnsi name = token.ToString();
|
||||
|
||||
// Read '=' character
|
||||
if (token.Separator != Separator('='))
|
||||
{
|
||||
if (token.Separator.IsWhiteSpace())
|
||||
text.EatWhiteSpaces();
|
||||
if (text.PeekChar() != '=')
|
||||
{
|
||||
parser->OnError(TEXT("Incorrect shader permutation. Missing \'='\' character for definition value."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Read definition value
|
||||
text.ReadToken(&token);
|
||||
if (token.Length == 0)
|
||||
{
|
||||
parser->OnError(TEXT("Incorrect shader permutation. Definition value is empty."));
|
||||
return;
|
||||
}
|
||||
StringAnsi value = token.ToString();
|
||||
|
||||
// Read ',' or ')' character (depends on parameter index)
|
||||
char checkChar = (paramIndex == _startTokenPermutationSize - 1) ? ')' : ',';
|
||||
if (token.Separator != Separator(checkChar))
|
||||
{
|
||||
parser->OnError(TEXT("Incorrect shader permutation declaration."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if hasn't been already defined in that permutation
|
||||
if (current.HasDefinition(permutationIndex, name))
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Incorrect shader function permutation definition. Already defined \'{0}\'."), String(name)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add entry to the meta
|
||||
permutation.Entries.Add({ name, value });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shader function flag reader
|
||||
/// </summary>
|
||||
class FlagReader : public ITokenReader
|
||||
{
|
||||
protected:
|
||||
|
||||
ShaderFunctionReader* _parent;
|
||||
Token _startToken;
|
||||
|
||||
public:
|
||||
|
||||
FlagReader(ShaderFunctionReader* parent)
|
||||
: _parent(parent)
|
||||
, _startToken("META_FLAG")
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [ITokenReader]
|
||||
bool CheckStartToken(const Token& token) override
|
||||
{
|
||||
return token == _startToken;
|
||||
}
|
||||
|
||||
void Process(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
auto& current = _parent->_current;
|
||||
|
||||
// Shader Flag type
|
||||
text.ReadToken(&token);
|
||||
current.Flags |= ParseShaderFlags(token);
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
PermutationReader* _permutationReader;
|
||||
|
||||
ShaderFunctionReader()
|
||||
{
|
||||
_childReaders.Add(_permutationReader = New<PermutationReader>(this));
|
||||
_childReaders.Add(New<FlagReader>(this));
|
||||
}
|
||||
|
||||
~ShaderFunctionReader()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// [ShaderMetaReader]
|
||||
void OnParseBefore(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
Token token;
|
||||
|
||||
// Clear current meta
|
||||
_current.Name.Clear();
|
||||
_current.Permutations.Clear();
|
||||
_current.Flags = ShaderFlags::Default;
|
||||
_current.MinFeatureLevel = FeatureLevel::ES2;
|
||||
_permutationReader->Clear();
|
||||
|
||||
// Here we read '(x, y)\n' where 'x' is a shader function 'visible' flag, and 'y' is mini feature level
|
||||
text.ReadToken(&token);
|
||||
token = parser->GetMacros().GetValue(token);
|
||||
if (token == "true" || token == "1")
|
||||
{
|
||||
// Visible shader
|
||||
}
|
||||
else if (token == "false" || token == "0")
|
||||
{
|
||||
// Hidden shader
|
||||
_current.Flags = ShaderFlags::Hidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
parser->OnError(TEXT("Invalid shader function \'isVisible\' option value."));
|
||||
return;
|
||||
}
|
||||
text.ReadToken(&token);
|
||||
token = parser->GetMacros().GetValue(token);
|
||||
struct MinFeatureLevel
|
||||
{
|
||||
FeatureLevel Level;
|
||||
const char* Token;
|
||||
};
|
||||
MinFeatureLevel levels[] =
|
||||
{
|
||||
{ FeatureLevel::ES2, "FEATURE_LEVEL_ES2" },
|
||||
{ FeatureLevel::ES3, "FEATURE_LEVEL_ES3" },
|
||||
{ FeatureLevel::ES3_1, "FEATURE_LEVEL_ES3_1" },
|
||||
{ FeatureLevel::SM4, "FEATURE_LEVEL_SM4" },
|
||||
{ FeatureLevel::SM5, "FEATURE_LEVEL_SM5" },
|
||||
};
|
||||
bool missing = true;
|
||||
for (int32 i = 0; i < ARRAY_COUNT(levels); i++)
|
||||
{
|
||||
if (token == levels[i].Token)
|
||||
{
|
||||
_current.MinFeatureLevel = levels[i].Level;
|
||||
missing = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (missing)
|
||||
{
|
||||
parser->OnError(TEXT("Invalid shader function \'minFeatureLevel\' option value."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Read rest of the line
|
||||
text.ReadLine();
|
||||
}
|
||||
|
||||
void OnParseAfter(IShaderParser* parser, Reader& text) override
|
||||
{
|
||||
// Validate amount of permutations
|
||||
if (_current.Permutations.Count() > SHADER_PERMUTATIONS_MAX_COUNT)
|
||||
{
|
||||
parser->OnError(String::Format(TEXT("Function \'{0}\' uses too many permutations. Maximum allowed amount is {1}."), String(_current.Name), SHADER_PERMUTATIONS_MAX_COUNT));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if shader has no permutations
|
||||
if (_current.Permutations.IsEmpty())
|
||||
{
|
||||
// Just add blank permutation (rest of the code will work without any hacks for empty permutations list)
|
||||
_current.Permutations.Add(ShaderPermutation());
|
||||
}
|
||||
|
||||
// Check if use this shader program
|
||||
if ((_current.Flags & ShaderFlags::Hidden) == false && _current.MinFeatureLevel <= parser->GetFeatureLevel())
|
||||
{
|
||||
// Cache read function
|
||||
_cache.Add(_current);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define DECLARE_SHADER_META_READER_HEADER(tokenName, shaderMetaMemberCollection) public: \
|
||||
bool CheckStartToken(const Token& token) override \
|
||||
{ return token == tokenName; } \
|
||||
void FlushCache(IShaderParser* parser, ShaderMeta* result) \
|
||||
{ result->shaderMetaMemberCollection.Add(_cache); }
|
||||
|
||||
#endif
|
||||
377
Source/Engine/ShadersCompilation/Parser/ShaderMeta.h
Normal file
377
Source/Engine/ShadersCompilation/Parser/ShaderMeta.h
Normal file
@@ -0,0 +1,377 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Core/Config.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Engine/Graphics/PixelFormat.h"
|
||||
#include "Config.h"
|
||||
|
||||
struct ShaderPermutationEntry
|
||||
{
|
||||
StringAnsi Name;
|
||||
StringAnsi Value;
|
||||
};
|
||||
|
||||
struct ShaderPermutation
|
||||
{
|
||||
// TODO: maybe we could use macro SHADER_PERMUTATIONS_MAX_PARAMS_COUNT and reduce amount of dynamic allocations for permutations
|
||||
Array<ShaderPermutationEntry> Entries;
|
||||
|
||||
Array<char> DebugData;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shader function metadata
|
||||
/// </summary>
|
||||
class ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor
|
||||
/// </summary>
|
||||
virtual ~ShaderFunctionMeta() = default;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Function name
|
||||
/// </summary>
|
||||
StringAnsi Name;
|
||||
|
||||
/// <summary>
|
||||
/// Function flags.
|
||||
/// </summary>
|
||||
ShaderFlags Flags;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum graphics platform feature level to support this shader.
|
||||
/// </summary>
|
||||
FeatureLevel MinFeatureLevel;
|
||||
|
||||
/// <summary>
|
||||
/// All possible values for the permutations and it's values to generate different permutation of this function
|
||||
/// </summary>
|
||||
Array<ShaderPermutation> Permutations;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Checks if definition name has been added to the given permutation
|
||||
/// </summary>
|
||||
/// <param name="permutationIndex">Shader permutation index</param>
|
||||
/// <param name="defineName">Name of the definition to check</param>
|
||||
/// <returns>True if definition of given name has been registered for the permutations, otherwise false</returns>
|
||||
bool HasDefinition(int32 permutationIndex, const StringAnsi& defineName) const
|
||||
{
|
||||
ASSERT(Math::IsInRange(permutationIndex, 0, Permutations.Count() - 1));
|
||||
|
||||
auto& permutation = Permutations[permutationIndex];
|
||||
for (int32 i = 0; i < permutation.Entries.Count(); i++)
|
||||
{
|
||||
if (permutation.Entries[i].Name == defineName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if definition name has been added to the permutations collection
|
||||
/// </summary>
|
||||
/// <param name="defineName">Name of the definition to check</param>
|
||||
/// <returns>True if definition of given name has been registered for the permutations, otherwise false</returns>
|
||||
bool HasDefinition(const StringAnsi& defineName) const
|
||||
{
|
||||
for (int32 permutationIndex = 0; permutationIndex < Permutations.Count(); permutationIndex++)
|
||||
{
|
||||
if (HasDefinition(permutationIndex, defineName))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all macros for given shader permutation
|
||||
/// </summary>
|
||||
/// <param name="permutationIndex">Shader permutation index</param>
|
||||
/// <param name="macros">Output array with permutation macros</param>
|
||||
void GetDefinitionsForPermutation(int32 permutationIndex, Array<ShaderMacro>& macros) const
|
||||
{
|
||||
ASSERT(Math::IsInRange(permutationIndex, 0, Permutations.Count() - 1));
|
||||
|
||||
auto& permutation = Permutations[permutationIndex];
|
||||
for (int32 i = 0; i < permutation.Entries.Count(); i++)
|
||||
{
|
||||
auto& e = permutation.Entries[i];
|
||||
macros.Add({
|
||||
e.Name.Get(),
|
||||
e.Value.Get()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets shader function meta stage type
|
||||
/// </summary>
|
||||
/// <returns>Shader Stage type</returns>
|
||||
virtual ShaderStage GetStage() const = 0;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Vertex shader function meta
|
||||
/// </summary>
|
||||
class VertexShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Input element type
|
||||
/// </summary>
|
||||
enum class InputType : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
POSITION = 1,
|
||||
COLOR = 2,
|
||||
TEXCOORD = 3,
|
||||
NORMAL = 4,
|
||||
TANGENT = 5,
|
||||
BITANGENT = 6,
|
||||
ATTRIBUTE = 7,
|
||||
BLENDINDICES = 8,
|
||||
BLENDWEIGHT = 9,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Input element
|
||||
/// </summary>
|
||||
struct InputElement
|
||||
{
|
||||
/// <summary>
|
||||
/// Semantic type
|
||||
/// </summary>
|
||||
InputType Type;
|
||||
|
||||
/// <summary>
|
||||
/// Semantic index
|
||||
/// </summary>
|
||||
byte Index;
|
||||
|
||||
/// <summary>
|
||||
/// Element data format
|
||||
/// </summary>
|
||||
PixelFormat Format;
|
||||
|
||||
/// <summary>
|
||||
/// An integer value that identifies the input-assembler
|
||||
/// </summary>
|
||||
byte InputSlot;
|
||||
|
||||
/// <summary>
|
||||
/// Optional. Offset (in bytes) between each element. Use INPUT_LAYOUT_ELEMENT_ALIGN for convenience to define the current element directly after the previous one, including any packing if necessary
|
||||
/// </summary>
|
||||
uint32 AlignedByteOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Identifies the input data class for a single input slot. INPUT_LAYOUT_ELEMENT_PER_VERTEX_DATA or INPUT_LAYOUT_ELEMENT_PER_INSTANCE_DATA
|
||||
/// </summary>
|
||||
byte InputSlotClass;
|
||||
|
||||
/// <summary>
|
||||
/// The number of instances to draw using the same per-instance data before advancing in the buffer by one element. This value must be 0 for an element that contains per-vertex data
|
||||
/// </summary>
|
||||
uint32 InstanceDataStepRate;
|
||||
|
||||
/// <summary>
|
||||
/// The visible flag expression. Allows to show/hide element from the input layout based on input macros (also from permutation macros). use empty value to skip this feature.
|
||||
/// </summary>
|
||||
StringAnsi VisibleFlag;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Input layout description
|
||||
/// </summary>
|
||||
Array<InputElement> InputLayout;
|
||||
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Vertex;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Hull (or tessellation control) shader function meta
|
||||
/// </summary>
|
||||
class HullShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The input control points count (valid range: 1-32).
|
||||
/// </summary>
|
||||
int32 ControlPointsCount;
|
||||
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Hull;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Domain (or tessellation evaluation) shader function meta
|
||||
/// </summary>
|
||||
class DomainShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Domain;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Geometry shader function meta
|
||||
/// </summary>
|
||||
class GeometryShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Geometry;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Pixel shader function meta
|
||||
/// </summary>
|
||||
class PixelShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Pixel;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Compute shader function meta
|
||||
/// </summary>
|
||||
class ComputeShaderMeta : public ShaderFunctionMeta
|
||||
{
|
||||
public:
|
||||
|
||||
// [ShaderFunctionMeta]
|
||||
ShaderStage GetStage() const override
|
||||
{
|
||||
return ShaderStage::Compute;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Constant buffer meta
|
||||
/// </summary>
|
||||
struct ConstantBufferMeta
|
||||
{
|
||||
/// <summary>
|
||||
/// Slot index
|
||||
/// </summary>
|
||||
byte Slot;
|
||||
|
||||
/// <summary>
|
||||
/// Buffer name
|
||||
/// </summary>
|
||||
StringAnsi Name;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Shader source metadata
|
||||
/// </summary>
|
||||
class ShaderMeta
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Vertex Shaders
|
||||
/// </summary>
|
||||
Array<VertexShaderMeta> VS;
|
||||
|
||||
/// <summary>
|
||||
/// Hull Shaders
|
||||
/// </summary>
|
||||
Array<HullShaderMeta> HS;
|
||||
|
||||
/// <summary>
|
||||
/// Domain Shaders
|
||||
/// </summary>
|
||||
Array<DomainShaderMeta> DS;
|
||||
|
||||
/// <summary>
|
||||
/// Geometry Shaders
|
||||
/// </summary>
|
||||
Array<GeometryShaderMeta> GS;
|
||||
|
||||
/// <summary>
|
||||
/// Pixel Shaders
|
||||
/// </summary>
|
||||
Array<PixelShaderMeta> PS;
|
||||
|
||||
/// <summary>
|
||||
/// Compute Shaders
|
||||
/// </summary>
|
||||
Array<ComputeShaderMeta> CS;
|
||||
|
||||
/// <summary>
|
||||
/// Constant Buffers
|
||||
/// </summary>
|
||||
Array<ConstantBufferMeta> CB;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets amount of shaders attached (not counting permutations)
|
||||
/// </summary>
|
||||
/// <returns>Amount of all shader programs</returns>
|
||||
uint32 GetShadersCount() const
|
||||
{
|
||||
return VS.Count() + HS.Count() + DS.Count() + GS.Count() + PS.Count() + CS.Count();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all shader functions (all types)
|
||||
/// </summary>
|
||||
/// <param name="functions">Output collections of functions</param>
|
||||
void GetShaders(Array<const ShaderFunctionMeta*>& functions) const
|
||||
{
|
||||
#define PEEK_SHADERS(collection) for (int32 i = 0; i < collection.Count(); i++) functions.Add(dynamic_cast<const ShaderFunctionMeta*>(&(collection[i])));
|
||||
PEEK_SHADERS(VS);
|
||||
PEEK_SHADERS(HS);
|
||||
PEEK_SHADERS(DS);
|
||||
PEEK_SHADERS(GS);
|
||||
PEEK_SHADERS(PS);
|
||||
PEEK_SHADERS(CS);
|
||||
#undef PEEK_SHADERS
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ShaderProcessing.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Core/Log.h"
|
||||
|
||||
VertexShaderMeta::InputType ShaderProcessing::ParseInputType(const Token& token)
|
||||
{
|
||||
struct InputDesc
|
||||
{
|
||||
VertexShaderMeta::InputType e;
|
||||
const char* s;
|
||||
};
|
||||
|
||||
#define _PARSE_ENTRY(x) { VertexShaderMeta::InputType::x, #x }
|
||||
const InputDesc formats[] =
|
||||
{
|
||||
_PARSE_ENTRY(Invalid),
|
||||
_PARSE_ENTRY(POSITION),
|
||||
_PARSE_ENTRY(COLOR),
|
||||
_PARSE_ENTRY(TEXCOORD),
|
||||
_PARSE_ENTRY(NORMAL),
|
||||
_PARSE_ENTRY(TANGENT),
|
||||
_PARSE_ENTRY(BITANGENT),
|
||||
_PARSE_ENTRY(ATTRIBUTE),
|
||||
_PARSE_ENTRY(BLENDINDICES),
|
||||
_PARSE_ENTRY(BLENDWEIGHT),
|
||||
};
|
||||
#undef _PARSE_ENTRY
|
||||
|
||||
for (int32 i = 0; i < ARRAY_COUNT(formats); i++)
|
||||
{
|
||||
if (token == formats[i].s)
|
||||
return formats[i].e;
|
||||
}
|
||||
|
||||
return VertexShaderMeta::InputType::Invalid;
|
||||
}
|
||||
|
||||
PixelFormat ShaderProcessing::ParsePixelFormat(const Token& token)
|
||||
{
|
||||
struct DataDesc
|
||||
{
|
||||
PixelFormat e;
|
||||
const char* s;
|
||||
};
|
||||
|
||||
#define _PARSE_ENTRY(x) { PixelFormat::x, #x }
|
||||
const DataDesc formats[] =
|
||||
{
|
||||
_PARSE_ENTRY(Unknown),
|
||||
_PARSE_ENTRY(R32G32B32A32_Float),
|
||||
_PARSE_ENTRY(R32G32B32A32_UInt),
|
||||
_PARSE_ENTRY(R32G32B32A32_SInt),
|
||||
_PARSE_ENTRY(R32G32B32_Float),
|
||||
_PARSE_ENTRY(R32G32B32_UInt),
|
||||
_PARSE_ENTRY(R32G32B32_SInt),
|
||||
_PARSE_ENTRY(R16G16B16A16_Float),
|
||||
_PARSE_ENTRY(R16G16B16A16_UNorm),
|
||||
_PARSE_ENTRY(R16G16B16A16_UInt),
|
||||
_PARSE_ENTRY(R16G16B16A16_SNorm),
|
||||
_PARSE_ENTRY(R16G16B16A16_SInt),
|
||||
_PARSE_ENTRY(R32G32_Float),
|
||||
_PARSE_ENTRY(R32G32_UInt),
|
||||
_PARSE_ENTRY(R32G32_SInt),
|
||||
_PARSE_ENTRY(R10G10B10A2_UNorm),
|
||||
_PARSE_ENTRY(R10G10B10A2_UInt),
|
||||
_PARSE_ENTRY(R11G11B10_Float),
|
||||
_PARSE_ENTRY(R8G8B8A8_UNorm),
|
||||
_PARSE_ENTRY(R8G8B8A8_UNorm_sRGB),
|
||||
_PARSE_ENTRY(R8G8B8A8_UInt),
|
||||
_PARSE_ENTRY(R8G8B8A8_SNorm),
|
||||
_PARSE_ENTRY(R8G8B8A8_SInt),
|
||||
_PARSE_ENTRY(R16G16_Float),
|
||||
_PARSE_ENTRY(R16G16_UNorm),
|
||||
_PARSE_ENTRY(R16G16_UInt),
|
||||
_PARSE_ENTRY(R16G16_SNorm),
|
||||
_PARSE_ENTRY(R16G16_SInt),
|
||||
_PARSE_ENTRY(R32_Float),
|
||||
_PARSE_ENTRY(R32_UInt),
|
||||
_PARSE_ENTRY(R32_SInt),
|
||||
_PARSE_ENTRY(R8G8_UNorm),
|
||||
_PARSE_ENTRY(R8G8_UInt),
|
||||
_PARSE_ENTRY(R8G8_SNorm),
|
||||
_PARSE_ENTRY(R8G8_SInt),
|
||||
_PARSE_ENTRY(R16_Float),
|
||||
_PARSE_ENTRY(R16_UNorm),
|
||||
_PARSE_ENTRY(R16_UInt),
|
||||
_PARSE_ENTRY(R16_SNorm),
|
||||
_PARSE_ENTRY(R16_SInt),
|
||||
_PARSE_ENTRY(R8_UNorm),
|
||||
_PARSE_ENTRY(R8_UInt),
|
||||
_PARSE_ENTRY(R8_SNorm),
|
||||
_PARSE_ENTRY(R8_SInt),
|
||||
_PARSE_ENTRY(A8_UNorm),
|
||||
_PARSE_ENTRY(R1_UNorm),
|
||||
_PARSE_ENTRY(R8G8_B8G8_UNorm),
|
||||
_PARSE_ENTRY(G8R8_G8B8_UNorm),
|
||||
_PARSE_ENTRY(BC1_UNorm),
|
||||
_PARSE_ENTRY(BC1_UNorm_sRGB),
|
||||
_PARSE_ENTRY(BC2_UNorm),
|
||||
_PARSE_ENTRY(BC2_UNorm_sRGB),
|
||||
_PARSE_ENTRY(BC3_UNorm),
|
||||
_PARSE_ENTRY(BC3_UNorm_sRGB),
|
||||
_PARSE_ENTRY(BC4_UNorm),
|
||||
_PARSE_ENTRY(BC4_SNorm),
|
||||
_PARSE_ENTRY(BC5_UNorm),
|
||||
_PARSE_ENTRY(BC5_SNorm),
|
||||
_PARSE_ENTRY(B5G6R5_UNorm),
|
||||
_PARSE_ENTRY(B5G5R5A1_UNorm),
|
||||
_PARSE_ENTRY(B8G8R8A8_UNorm),
|
||||
_PARSE_ENTRY(B8G8R8X8_UNorm),
|
||||
_PARSE_ENTRY(B8G8R8A8_UNorm_sRGB),
|
||||
_PARSE_ENTRY(B8G8R8X8_UNorm_sRGB),
|
||||
_PARSE_ENTRY(BC6H_Uf16),
|
||||
_PARSE_ENTRY(BC6H_Sf16),
|
||||
_PARSE_ENTRY(BC7_UNorm),
|
||||
_PARSE_ENTRY(BC7_UNorm_sRGB),
|
||||
};
|
||||
#undef _PARSE_ENTRY
|
||||
|
||||
for (int32 i = 0; i < ARRAY_COUNT(formats); i++)
|
||||
{
|
||||
if (token.EqualsIgnoreCase(formats[i].s))
|
||||
return formats[i].e;
|
||||
}
|
||||
|
||||
return PixelFormat::Unknown;
|
||||
}
|
||||
|
||||
ShaderFlags ShaderProcessing::ParseShaderFlags(const Token& token)
|
||||
{
|
||||
struct DataDesc
|
||||
{
|
||||
ShaderFlags e;
|
||||
const char* s;
|
||||
};
|
||||
|
||||
#define _PARSE_ENTRY(x) { ShaderFlags::x, #x }
|
||||
const DataDesc data[] =
|
||||
{
|
||||
_PARSE_ENTRY(Default),
|
||||
_PARSE_ENTRY(Hidden),
|
||||
_PARSE_ENTRY(NoFastMath),
|
||||
_PARSE_ENTRY(VertexToGeometryShader),
|
||||
};
|
||||
static_assert(ARRAY_COUNT(data) == 4, "Invalid amount of Shader Flag data entries.");
|
||||
#undef _PARSE_ENTRY
|
||||
|
||||
for (int32 i = 0; i < ARRAY_COUNT(data); i++)
|
||||
{
|
||||
if (token.EqualsIgnoreCase(data[i].s))
|
||||
return data[i].e;
|
||||
}
|
||||
|
||||
return ShaderFlags::Default;
|
||||
}
|
||||
|
||||
#endif
|
||||
146
Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp
Normal file
146
Source/Engine/ShadersCompilation/Parser/ShaderProcessing.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "ShaderProcessing.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
#include "Engine/Utilities/TextProcessing.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "ShaderFunctionReader.CB.h"
|
||||
#include "ShaderFunctionReader.VS.h"
|
||||
#include "ShaderFunctionReader.HS.h"
|
||||
#include "ShaderFunctionReader.DS.h"
|
||||
#include "ShaderFunctionReader.GS.h"
|
||||
#include "ShaderFunctionReader.PS.h"
|
||||
#include "ShaderFunctionReader.CS.h"
|
||||
#include "Config.h"
|
||||
|
||||
ShaderProcessing::Parser::Parser(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel)
|
||||
: failed(false)
|
||||
, targetName(targetName)
|
||||
, text(source, sourceLength)
|
||||
, _macros(macros)
|
||||
, _featureLevel(featureLevel)
|
||||
{
|
||||
}
|
||||
|
||||
ShaderProcessing::Parser::~Parser()
|
||||
{
|
||||
}
|
||||
|
||||
bool ShaderProcessing::Parser::Process(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel, ShaderMeta* result)
|
||||
{
|
||||
Parser parser(targetName, source, sourceLength, macros, featureLevel);
|
||||
parser.Process(result);
|
||||
return parser.Failed();
|
||||
}
|
||||
|
||||
void ShaderProcessing::Parser::Process(ShaderMeta* result)
|
||||
{
|
||||
init();
|
||||
|
||||
failed = process();
|
||||
|
||||
if (!failed)
|
||||
failed = collectResults(result);
|
||||
}
|
||||
|
||||
void ShaderProcessing::Parser::init()
|
||||
{
|
||||
// Init text processing tokens for hlsl language
|
||||
text.Setup_HLSL();
|
||||
|
||||
// Init shader functions readers
|
||||
_childReaders.Add(New<ConstantBufferReader>());
|
||||
_childReaders.Add(New<VertexShaderFunctionReader>());
|
||||
_childReaders.Add(New<HullShaderFunctionReader>());
|
||||
_childReaders.Add(New<DomainShaderFunctionReader>());
|
||||
_childReaders.Add(New<GeometryShaderFunctionReader>());
|
||||
_childReaders.Add(New<PixelShaderFunctionReader>());
|
||||
_childReaders.Add(New<ComputeShaderFunctionReader>());
|
||||
}
|
||||
|
||||
bool ShaderProcessing::Parser::process()
|
||||
{
|
||||
const Token defineToken("#define");
|
||||
const Separator singleLineCommentSeparator('/', '/');
|
||||
const Separator multiLineCommentSeparator('/', '*');
|
||||
|
||||
// TODO: split parsing into two phrases: comments preprocessing and parsing
|
||||
|
||||
// Read whole source code
|
||||
Token token;
|
||||
while (text.CanRead())
|
||||
{
|
||||
text.ReadToken(&token);
|
||||
|
||||
// Single line comment
|
||||
if (token.Separator == singleLineCommentSeparator)
|
||||
{
|
||||
// Read whole line
|
||||
text.ReadLine();
|
||||
}
|
||||
// Multi line comment
|
||||
else if (token.Separator == multiLineCommentSeparator)
|
||||
{
|
||||
// Read tokens until end sequence
|
||||
char prev = ' ';
|
||||
char c;
|
||||
while (text.CanRead())
|
||||
{
|
||||
c = text.ReadChar();
|
||||
if (prev == '*' && c == '/')
|
||||
{
|
||||
break;
|
||||
}
|
||||
prev = c;
|
||||
}
|
||||
|
||||
// Check if comment is valid (has end before file end)
|
||||
if (!text.CanRead())
|
||||
{
|
||||
OnWarning(TEXT("Missing multiline comment ending"));
|
||||
}
|
||||
}
|
||||
// Preprocessor definition
|
||||
else if (token == defineToken)
|
||||
{
|
||||
// Skip
|
||||
text.ReadLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call children
|
||||
ProcessChildren(token, this);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderProcessing::Parser::collectResults(ShaderMeta* result)
|
||||
{
|
||||
// Collect results from all the readers
|
||||
for (int32 i = 0; i < _childReaders.Count(); i++)
|
||||
_childReaders[i]->CollectResults(this, result);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderProcessing::Parser::OnError(const String& message)
|
||||
{
|
||||
// Set flag
|
||||
failed = true;
|
||||
|
||||
// Send event
|
||||
LOG(Error, "Processing shader '{0}' error at line {1}. {2}", targetName, text.GetLine(), message);
|
||||
}
|
||||
|
||||
void ShaderProcessing::Parser::OnWarning(const String& message)
|
||||
{
|
||||
// Send event
|
||||
LOG(Warning, "Processing shader '{0}' warning at line {1}. {2}", targetName, text.GetLine(), message);
|
||||
}
|
||||
|
||||
#endif
|
||||
93
Source/Engine/ShadersCompilation/Parser/ShaderProcessing.h
Normal file
93
Source/Engine/ShadersCompilation/Parser/ShaderProcessing.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderMeta.h"
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "IShaderFunctionReader.h"
|
||||
#include "ITokenReadersContainer.h"
|
||||
|
||||
namespace ShaderProcessing
|
||||
{
|
||||
extern VertexShaderMeta::InputType ParseInputType(const Token& token);
|
||||
extern PixelFormat ParsePixelFormat(const Token& token);
|
||||
extern ShaderFlags ParseShaderFlags(const Token& token);
|
||||
|
||||
/// <summary>
|
||||
/// Shader files meta data processing tool
|
||||
/// </summary>
|
||||
class Parser : public IShaderParser, public ITokenReadersContainerBase<IShaderFunctionReader>
|
||||
{
|
||||
private:
|
||||
|
||||
bool failed;
|
||||
String targetName;
|
||||
Reader text;
|
||||
ParserMacros _macros;
|
||||
FeatureLevel _featureLevel;
|
||||
|
||||
private:
|
||||
|
||||
Parser(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel);
|
||||
~Parser();
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Process shader source code and generate metadata
|
||||
/// </summary>
|
||||
/// <param name="targetName">Calling object name (used for warnings/errors logging)</param>
|
||||
/// <param name="source">ANSI source code</param>
|
||||
/// <param name="sourceLength">Amount of characters in the source code</param>
|
||||
/// <param name="macros">The input macros.</param>
|
||||
/// <param name="featureLevel">The target feature level.</param>
|
||||
/// <param name="result">Output result with metadata</param>
|
||||
/// <returns>True if cannot process the file (too many errors), otherwise false</returns>
|
||||
static bool Process(const String& targetName, const char* source, int32 sourceLength, ParserMacros macros, FeatureLevel featureLevel, ShaderMeta* result);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Process shader source code and generate metadata
|
||||
/// </summary>
|
||||
/// <param name="result">Output result with metadata</param>
|
||||
void Process(ShaderMeta* result);
|
||||
|
||||
private:
|
||||
|
||||
void init();
|
||||
bool process();
|
||||
bool collectResults(ShaderMeta* result);
|
||||
|
||||
public:
|
||||
|
||||
// [IShaderParser]
|
||||
FeatureLevel GetFeatureLevel() const override
|
||||
{
|
||||
return _featureLevel;
|
||||
}
|
||||
|
||||
bool Failed() const override
|
||||
{
|
||||
return failed;
|
||||
}
|
||||
|
||||
Reader& GetReader() override
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
ParserMacros GetMacros() const override
|
||||
{
|
||||
return _macros;
|
||||
}
|
||||
|
||||
void OnError(const String& message) override;
|
||||
void OnWarning(const String& message) override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilationContext.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Parser/ShaderMeta.h"
|
||||
#include "Engine/Graphics/Config.h"
|
||||
#include "Config.h"
|
||||
|
||||
void ShaderCompilationContext::OnError(const char* message)
|
||||
{
|
||||
LOG(Error, "Failed to compile '{0}'. {1}", Options->TargetName, String(message));
|
||||
}
|
||||
|
||||
void ShaderCompilationContext::OnCollectDebugInfo(ShaderFunctionMeta& meta, int32 permutationIndex, const char* data, const int32 dataLength)
|
||||
{
|
||||
#ifdef GPU_USE_SHADERS_DEBUG_LAYER
|
||||
|
||||
// Cache data
|
||||
meta.Permutations[permutationIndex].DebugData.Set(data, dataLength);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
ShaderCompilationContext::ShaderCompilationContext(const ShaderCompilationOptions* options, ShaderMeta* meta)
|
||||
: Options(options)
|
||||
, Meta(meta)
|
||||
, Output(options->Output)
|
||||
{
|
||||
// Convert target name to ANSI text (with limited length)
|
||||
const int32 ansiNameLen = Math::Min<int32>(ARRAY_COUNT(TargetNameAnsi) - 1, options->TargetName.Length());
|
||||
StringUtils::ConvertUTF162ANSI(*options->TargetName, TargetNameAnsi, ansiNameLen);
|
||||
TargetNameAnsi[ansiNameLen] = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
77
Source/Engine/ShadersCompilation/ShaderCompilationContext.h
Normal file
77
Source/Engine/ShadersCompilation/ShaderCompilationContext.h
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Config.h"
|
||||
#include "Engine/Core/Collections/HashSet.h"
|
||||
|
||||
class ShaderMeta;
|
||||
class ShaderFunctionMeta;
|
||||
class MemoryWriteStream;
|
||||
|
||||
/// <summary>
|
||||
/// Shader compilation context container
|
||||
/// </summary>
|
||||
class ShaderCompilationContext
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// The compilation options.
|
||||
/// </summary>
|
||||
const ShaderCompilationOptions* Options;
|
||||
|
||||
/// <summary>
|
||||
/// The shader metadata container.
|
||||
/// </summary>
|
||||
ShaderMeta* Meta;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Output stream to write compiled shader cache to.
|
||||
/// </summary>
|
||||
MemoryWriteStream* Output;
|
||||
|
||||
/// <summary>
|
||||
/// All source files included by this file (absolute paths). Generated during shader compilation.
|
||||
/// </summary>
|
||||
HashSet<String> Includes;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Name of the target object (in ASCII)
|
||||
/// </summary>
|
||||
char TargetNameAnsi[64];
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Event called on compilation error
|
||||
/// </summary>
|
||||
/// <param name="message">Error message</param>
|
||||
void OnError(const char* message);
|
||||
|
||||
/// <summary>
|
||||
/// Event called on compilation debug data collecting
|
||||
/// </summary>
|
||||
/// <param name="meta">Target function meta</param>
|
||||
/// <param name="permutationIndex">Permutation index</param>
|
||||
/// <param name="data">Data pointer</param>
|
||||
/// <param name="dataLength">Data size in bytes</param>
|
||||
void OnCollectDebugInfo(ShaderFunctionMeta& meta, int32 permutationIndex, const char* data, const int32 dataLength);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Init
|
||||
/// </summary>
|
||||
/// <param name="options">Options</param>
|
||||
/// <param name="meta">Metadata</param>
|
||||
ShaderCompilationContext(const ShaderCompilationOptions* options, ShaderMeta* meta);
|
||||
};
|
||||
|
||||
#endif
|
||||
471
Source/Engine/ShadersCompilation/ShaderCompiler.cpp
Normal file
471
Source/Engine/ShadersCompilation/ShaderCompiler.cpp
Normal file
@@ -0,0 +1,471 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompiler.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Collections/Dictionary.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
|
||||
namespace IncludedFiles
|
||||
{
|
||||
struct File
|
||||
{
|
||||
String Path;
|
||||
DateTime LastEditTime;
|
||||
Array<byte> Source;
|
||||
};
|
||||
|
||||
CriticalSection Locker;
|
||||
Dictionary<String, File*> Files;
|
||||
|
||||
#if USE_EDITOR
|
||||
bool FindProject(const ProjectInfo* project, HashSet<const ProjectInfo*>& projects, const StringView& projectName, String& path)
|
||||
{
|
||||
if (!project || projects.Contains(project))
|
||||
return false;
|
||||
projects.Add(project);
|
||||
|
||||
// Check the project name
|
||||
if (project->Name == projectName)
|
||||
{
|
||||
path = project->ProjectFolderPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize referenced projects
|
||||
for (const auto& reference : project->References)
|
||||
{
|
||||
if (reference.Project && FindProject(reference.Project, projects, projectName, path))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ShaderCompiler::Compile(ShaderCompilationContext* context)
|
||||
{
|
||||
// Clear cache
|
||||
_globalMacros.Clear();
|
||||
_macros.Clear();
|
||||
_constantBuffers.Clear();
|
||||
_globalMacros.EnsureCapacity(32);
|
||||
_macros.EnsureCapacity(32);
|
||||
_context = context;
|
||||
|
||||
// Prepare
|
||||
auto output = context->Output;
|
||||
auto meta = context->Meta;
|
||||
const int32 shadersCount = meta->GetShadersCount();
|
||||
if (OnCompileBegin())
|
||||
return true;
|
||||
_globalMacros.Add({ nullptr, nullptr });
|
||||
|
||||
// Setup constant buffers cache
|
||||
_constantBuffers.EnsureCapacity(meta->CB.Count(), false);
|
||||
for (int32 i = 0; i < meta->CB.Count(); i++)
|
||||
_constantBuffers.Add({ meta->CB[i].Slot, false, 0 });
|
||||
|
||||
// [Output] Version number
|
||||
output->WriteInt32(7);
|
||||
|
||||
// [Output] Additional data start
|
||||
const int32 additionalDataStartPos = output->GetPosition();
|
||||
output->WriteInt32(-1);
|
||||
|
||||
// [Output] Amount of shaders
|
||||
output->WriteInt32(shadersCount);
|
||||
|
||||
// Compile all shaders
|
||||
if (CompileShaders())
|
||||
return true;
|
||||
|
||||
// [Output] Constant Buffers
|
||||
{
|
||||
const int32 cbsCount = _constantBuffers.Count();
|
||||
ASSERT(cbsCount == meta->CB.Count());
|
||||
|
||||
// Find maximum used slot index
|
||||
byte maxCbSlot = 0;
|
||||
for (int32 i = 0; i < cbsCount; i++)
|
||||
{
|
||||
maxCbSlot = Math::Max(maxCbSlot, _constantBuffers[i].Slot);
|
||||
}
|
||||
|
||||
output->WriteByte(static_cast<byte>(cbsCount));
|
||||
output->WriteByte(maxCbSlot);
|
||||
// TODO: do we still need to serialize max cb slot?
|
||||
|
||||
for (int32 i = 0; i < cbsCount; i++)
|
||||
{
|
||||
output->WriteByte(_constantBuffers[i].Slot);
|
||||
output->WriteUint32(_constantBuffers[i].Size);
|
||||
}
|
||||
}
|
||||
|
||||
// Additional Data Start
|
||||
*(int32*)(output->GetHandle() + additionalDataStartPos) = output->GetPosition();
|
||||
|
||||
// [Output] Includes
|
||||
output->WriteInt32(context->Includes.Count());
|
||||
for (auto& include : context->Includes)
|
||||
{
|
||||
output->WriteString(include.Item, 11);
|
||||
const auto date = FileSystem::GetFileLastEditTime(include.Item);
|
||||
output->Write(&date);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::GetIncludedFileSource(ShaderCompilationContext* context, const char* sourceFile, const char* includedFile, const char*& source, int32& sourceLength)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
source = nullptr;
|
||||
sourceLength = 0;
|
||||
|
||||
// Skip to the last root start './' but preserve the leading one
|
||||
const int32 includedFileLength = StringUtils::Length(includedFile);
|
||||
for (int32 i = includedFileLength - 2; i >= 2; i--)
|
||||
{
|
||||
if (StringUtils::Compare(includedFile + i, "./", 2) == 0)
|
||||
{
|
||||
includedFile = includedFile + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ScopeLock lock(IncludedFiles::Locker);
|
||||
|
||||
// Find the included file path
|
||||
String path;
|
||||
#if USE_EDITOR
|
||||
if (StringUtils::Compare(includedFile, "./", 2) == 0)
|
||||
{
|
||||
int32 projectNameEnd = -1;
|
||||
for (int32 i = 2; i < includedFileLength; i++)
|
||||
{
|
||||
if (includedFile[i] == '/')
|
||||
{
|
||||
projectNameEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (projectNameEnd == -1)
|
||||
{
|
||||
LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), TEXT("Missing project name after root path."));
|
||||
return true;
|
||||
}
|
||||
const StringAsUTF16<120> projectName(includedFile + 2, projectNameEnd - 2);
|
||||
if (StringUtils::Compare(projectName.Get(), TEXT("FlaxPlatforms")) == 0)
|
||||
{
|
||||
// Hard-coded redirect to platform-specific includes
|
||||
path /= Globals::StartupFolder / TEXT("Source/Platforms");
|
||||
}
|
||||
else
|
||||
{
|
||||
HashSet<const ProjectInfo*> projects;
|
||||
if (!IncludedFiles::FindProject(Editor::Project, projects, StringView(projectName.Get(), projectNameEnd - 2), path))
|
||||
{
|
||||
LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), TEXT("Failed to find the project of the given name."));
|
||||
return true;
|
||||
}
|
||||
path /= TEXT("Source/Shaders/");
|
||||
}
|
||||
const StringAsUTF16<250> localPath(includedFile + projectNameEnd + 1, includedFileLength - projectNameEnd - 1);
|
||||
path /= localPath.Get();
|
||||
}
|
||||
#else
|
||||
if (StringUtils::Compare(includedFile, "./Flax/", 7) == 0)
|
||||
{
|
||||
// Engine project relative shader path
|
||||
const auto includedFileStr = String(includedFile + 6);
|
||||
path = Globals::StartupFolder / TEXT("Source/Shaders") / includedFileStr;
|
||||
}
|
||||
#endif
|
||||
else if (FileSystem::FileExists(path = String(includedFile)))
|
||||
{
|
||||
// Absolute shader path
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Error, "Unknown shader source file '{0}' included in '{1}'.{2}", String(includedFile), String(sourceFile), String::Empty);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try to reuse file
|
||||
IncludedFiles::File* result = nullptr;
|
||||
if (!IncludedFiles::Files.TryGet(path, result) || FileSystem::GetFileLastEditTime(path) > result->LastEditTime)
|
||||
{
|
||||
// Remove old one
|
||||
if (result)
|
||||
{
|
||||
Delete(result);
|
||||
IncludedFiles::Files.Remove(path);
|
||||
}
|
||||
|
||||
// Load file
|
||||
result = New<IncludedFiles::File>();
|
||||
result->Path = path;
|
||||
result->LastEditTime = FileSystem::GetFileLastEditTime(path);
|
||||
if (File::ReadAllBytes(result->Path, result->Source))
|
||||
{
|
||||
LOG(Error, "Failed to load shader source file '{0}' included in '{1}' (path: '{2}')", String(includedFile), String(sourceFile), path);
|
||||
Delete(result);
|
||||
return true;
|
||||
}
|
||||
IncludedFiles::Files.Add(path, result);
|
||||
}
|
||||
|
||||
context->Includes.Add(path);
|
||||
|
||||
// Copy to output
|
||||
source = (const char*)result->Source.Get();
|
||||
sourceLength = result->Source.Count() - 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderCompiler::DisposeIncludedFilesCache()
|
||||
{
|
||||
ScopeLock lock(IncludedFiles::Locker);
|
||||
|
||||
IncludedFiles::Files.ClearDelete();
|
||||
}
|
||||
|
||||
bool ShaderCompiler::CompileShaders()
|
||||
{
|
||||
auto meta = _context->Meta;
|
||||
|
||||
// Generate vertex shaders cache
|
||||
for (int32 i = 0; i < meta->VS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->VS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Vertex && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader, &WriteCustomDataVS))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate hull shaders cache
|
||||
for (int32 i = 0; i < meta->HS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->HS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Hull && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader, &WriteCustomDataHS))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate domain shaders cache
|
||||
for (int32 i = 0; i < meta->DS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->DS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Domain && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate geometry shaders cache
|
||||
for (int32 i = 0; i < meta->GS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->GS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Geometry && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate pixel shaders cache
|
||||
for (int32 i = 0; i < meta->PS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->PS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Pixel && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate compute shaders cache
|
||||
for (int32 i = 0; i < meta->CS.Count(); i++)
|
||||
{
|
||||
auto& shader = meta->CS[i];
|
||||
ASSERT(shader.GetStage() == ShaderStage::Compute && (shader.Flags & ShaderFlags::Hidden) == 0);
|
||||
if (CompileShader(shader))
|
||||
{
|
||||
LOG(Error, "Failed to compile \'{0}\'", String(shader.Name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::OnCompileBegin()
|
||||
{
|
||||
// Setup global macros
|
||||
static const char* Numbers[] =
|
||||
{
|
||||
// @formatter:off
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
|
||||
// @formatter:on
|
||||
};
|
||||
const auto profile = GetProfile();
|
||||
const auto featureLevel = RenderTools::GetFeatureLevel(profile);
|
||||
_globalMacros.Add({ "FEATURE_LEVEL", Numbers[(int32)featureLevel] });
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::OnCompileEnd()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta)
|
||||
{
|
||||
auto output = context->Output;
|
||||
|
||||
// [Output] Type
|
||||
output->WriteByte(static_cast<byte>(meta.GetStage()));
|
||||
|
||||
// [Output] Permutations count
|
||||
output->WriteByte(meta.Permutations.Count());
|
||||
|
||||
// [Output] Shader function name
|
||||
output->WriteStringAnsi(meta.Name, 11);
|
||||
|
||||
// [Output] Shader flags
|
||||
output->WriteUint32((uint32)meta.Flags);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize)
|
||||
{
|
||||
auto output = context->Output;
|
||||
|
||||
// [Output] Write compiled shader cache
|
||||
output->WriteUint32(cacheSize);
|
||||
output->WriteBytes(cache, cacheSize);
|
||||
|
||||
// [Output] Shader bindings meta
|
||||
output->Write(&bindings);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros)
|
||||
{
|
||||
auto output = context->Output;
|
||||
auto& metaVS = *(VertexShaderMeta*)&meta;
|
||||
auto& layout = metaVS.InputLayout;
|
||||
|
||||
// Get visible entries (based on `visible` flag switch)
|
||||
int32 layoutSize = 0;
|
||||
bool layoutVisible[VERTEX_SHADER_MAX_INPUT_ELEMENTS];
|
||||
for (int32 i = 0; i < layout.Count(); i++)
|
||||
{
|
||||
auto& element = layout[i];
|
||||
layoutVisible[i] = false;
|
||||
|
||||
// Parse using all input macros
|
||||
StringAnsi value = element.VisibleFlag;
|
||||
for (int32 j = 0; j < macros.Count() - 1; j++)
|
||||
{
|
||||
if (macros[j].Name == value)
|
||||
{
|
||||
value = macros[j].Definition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (value == "true" || value == "1")
|
||||
{
|
||||
// Visible
|
||||
layoutSize++;
|
||||
layoutVisible[i] = true;
|
||||
}
|
||||
else if (value == "false" || value == "0")
|
||||
{
|
||||
// Hidden
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG(Error, "Invalid vertex shader layout element \'visible\' option value.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// [Output] Input Layout
|
||||
output->WriteByte(static_cast<byte>(layoutSize));
|
||||
for (int32 a = 0; a < layout.Count(); a++)
|
||||
{
|
||||
auto& element = layout[a];
|
||||
if (!layoutVisible[a])
|
||||
continue;
|
||||
|
||||
// TODO: serialize whole struct?
|
||||
|
||||
output->WriteByte(static_cast<byte>(element.Type));
|
||||
output->WriteByte(element.Index);
|
||||
output->WriteByte(static_cast<byte>(element.Format));
|
||||
output->WriteByte(element.InputSlot);
|
||||
output->WriteUint32(element.AlignedByteOffset);
|
||||
output->WriteByte(element.InputSlotClass);
|
||||
output->WriteUint32(element.InstanceDataStepRate);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderCompiler::WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros)
|
||||
{
|
||||
auto output = context->Output;
|
||||
auto& metaHS = *(HullShaderMeta*)&meta;
|
||||
|
||||
// [Output] Control Points Count
|
||||
output->WriteInt32(metaHS.ControlPointsCount);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderCompiler::GetDefineForFunction(ShaderFunctionMeta& meta, Array<ShaderMacro>& macros)
|
||||
{
|
||||
auto& functionName = meta.Name;
|
||||
const int32 functionNameLength = static_cast<int32>(functionName.Length());
|
||||
_funcNameDefineBuffer.Clear();
|
||||
_funcNameDefineBuffer.EnsureCapacity(functionNameLength + 2);
|
||||
_funcNameDefineBuffer.Add('_');
|
||||
_funcNameDefineBuffer.Add(functionName.Get(), functionNameLength);
|
||||
_funcNameDefineBuffer.Add('\0');
|
||||
macros.Add({ _funcNameDefineBuffer.Get(), "1" });
|
||||
}
|
||||
|
||||
#endif
|
||||
106
Source/Engine/ShadersCompilation/ShaderCompiler.h
Normal file
106
Source/Engine/ShadersCompilation/ShaderCompiler.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilationContext.h"
|
||||
#include "Parser/ShaderMeta.h"
|
||||
#include "Engine/Graphics/Shaders/GPUShaderProgram.h"
|
||||
|
||||
/// <summary>
|
||||
/// Base class for the objects that can compile shaders source code.
|
||||
/// </summary>
|
||||
class ShaderCompiler
|
||||
{
|
||||
public:
|
||||
|
||||
struct ShaderResourceBuffer
|
||||
{
|
||||
byte Slot;
|
||||
bool IsUsed;
|
||||
uint32 Size;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
|
||||
protected:
|
||||
|
||||
ShaderProfile _profile;
|
||||
ShaderCompilationContext* _context = nullptr;
|
||||
Array<ShaderMacro> _globalMacros;
|
||||
Array<ShaderMacro> _macros;
|
||||
Array<ShaderResourceBuffer> _constantBuffers;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompiler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompiler(ShaderProfile profile)
|
||||
: _profile(profile)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ShaderCompiler"/> class.
|
||||
/// </summary>
|
||||
virtual ~ShaderCompiler() = default;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Gets shader profile supported by this compiler.
|
||||
/// </summary>
|
||||
/// <returns>The shader profile.</returns>
|
||||
FORCE_INLINE ShaderProfile GetProfile() const
|
||||
{
|
||||
return _profile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the shader compilation.
|
||||
/// </summary>
|
||||
/// <param name="context">The compilation context.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
bool Compile(ShaderCompilationContext* context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the included file source code. Handles system includes and absolute includes. Method is thread-safe.
|
||||
/// </summary>
|
||||
/// <param name="context">The compilation context.</param>
|
||||
/// <param name="sourceFile">The source file that is being compiled.</param>
|
||||
/// <param name="includedFile">The included file name (absolute or relative).</param>
|
||||
/// <param name="source">The output source code of the file (null-terminated), null if failed to load.</param>
|
||||
/// <param name="sourceLength">The output source code length of the file (characters count), 0 if failed to load.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool GetIncludedFileSource(ShaderCompilationContext* context, const char* sourceFile, const char* includedFile, const char*& source, int32& sourceLength);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache used by the shader includes.
|
||||
/// </summary>
|
||||
static void DisposeIncludedFilesCache();
|
||||
|
||||
protected:
|
||||
|
||||
typedef bool (*WritePermutationData)(ShaderCompilationContext*, ShaderFunctionMeta&, int32, const Array<ShaderMacro>&);
|
||||
|
||||
virtual bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) = 0;
|
||||
|
||||
bool CompileShaders();
|
||||
|
||||
virtual bool OnCompileBegin();
|
||||
virtual bool OnCompileEnd();
|
||||
|
||||
static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize);
|
||||
static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta);
|
||||
static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros);
|
||||
static bool WriteCustomDataHS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array<ShaderMacro>& macros);
|
||||
void GetDefineForFunction(ShaderFunctionMeta& meta, Array<ShaderMacro>& macros);
|
||||
};
|
||||
|
||||
#endif
|
||||
83
Source/Engine/ShadersCompilation/ShaderDebugDataExporter.h
Normal file
83
Source/Engine/ShadersCompilation/ShaderDebugDataExporter.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ShaderCompilationContext.h"
|
||||
#include "Engine/Graphics/Config.h"
|
||||
|
||||
#if GPU_USE_SHADERS_DEBUG_LAYER && COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/Core/Types/StringBuilder.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Parser/ShaderMeta.h"
|
||||
|
||||
/// <summary>
|
||||
/// Tool class used to export debug information about shaders
|
||||
/// </summary>
|
||||
class ShaderDebugDataExporter
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Exports compilation results info
|
||||
/// </summary>
|
||||
/// <param name="context">Compilation context</param>
|
||||
/// <returns>True if failed, otherwise false</returns>
|
||||
static bool Export(ShaderCompilationContext* context)
|
||||
{
|
||||
#if USE_EDITOR
|
||||
static String ShadersDebugInfoFolder = Globals::ProjectCacheFolder / TEXT("Shaders/Debug");
|
||||
#else
|
||||
static String ShadersDebugInfoFolder = Globals::ProductLocalFolder / TEXT("Shaders/Debug");
|
||||
#endif
|
||||
|
||||
// Create output folder
|
||||
if (!FileSystem::DirectoryExists(ShadersDebugInfoFolder))
|
||||
{
|
||||
if (FileSystem::CreateDirectory(ShadersDebugInfoFolder))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prepare
|
||||
auto options = context->Options;
|
||||
String outputFilePath = ShadersDebugInfoFolder / String::Format(TEXT("ShaderDebug_{0}_{1}.txt"), options->TargetName, options->TargetID);
|
||||
auto shadersCount = context->Meta->GetShadersCount();
|
||||
StringBuilder info(1024 * shadersCount);
|
||||
Array<const ShaderFunctionMeta*> functions(shadersCount);
|
||||
context->Meta->GetShaders(functions);
|
||||
|
||||
// Generate output info
|
||||
info.AppendFormat(TEXT("Target shader: {0} : {1}\nProfile: {2}\nCache size: {3} bytes\n"),
|
||||
options->TargetName,
|
||||
options->TargetID,
|
||||
::ToString(options->Profile),
|
||||
options->Output->GetPosition());
|
||||
for (int32 i = 0; i < functions.Count(); i++)
|
||||
{
|
||||
const auto f = functions[i];
|
||||
auto stageName = ::ToString(f->GetStage());
|
||||
for (int32 permutationIndex = 0; permutationIndex < f->Permutations.Count(); permutationIndex++)
|
||||
{
|
||||
info.Append(TEXT("\n*********************************************************************\n"));
|
||||
info.AppendFormat(TEXT("{0} Shader: {1}, Permutation: {2}\n"), stageName, String(f->Name), permutationIndex + 1);
|
||||
info.Append(f->Permutations[permutationIndex].DebugData.Get());
|
||||
}
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
|
||||
// Change line endings on Windows platform
|
||||
StringBuilder winNewLineFix = info;
|
||||
WindowsFileSystem::ConvertLineEndingsToDos(StringView(*winNewLineFix, winNewLineFix.Length()), info.GetCharArray());
|
||||
|
||||
#endif
|
||||
|
||||
// Write to file
|
||||
return File::WriteAllText(outputFilePath, info, Encoding::Unicode);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
65
Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs
Normal file
65
Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
using Flax.Build.Platforms;
|
||||
|
||||
/// <summary>
|
||||
/// Shader compiler module base class.
|
||||
/// </summary>
|
||||
public abstract class ShaderCompiler : EngineModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PrivateDefinitions.Add("COMPILE_WITH_SHADER_COMPILER");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shaders compilation module.
|
||||
/// </summary>
|
||||
public class ShadersCompilation : EngineModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_SHADER_COMPILER");
|
||||
|
||||
options.SourcePaths.Clear();
|
||||
options.SourceFiles.AddRange(Directory.GetFiles(FolderPath, "*.*", SearchOption.TopDirectoryOnly));
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Parser"));
|
||||
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
options.PrivateDependencies.Add("ShaderCompilerD3D");
|
||||
if (WindowsPlatformBase.GetSDKs().Any(x => x.Key != WindowsPlatformSDK.v8_1))
|
||||
options.PrivateDependencies.Add("ShaderCompilerDX");
|
||||
//options.PrivateDependencies.Add("ShaderCompilerOGL");
|
||||
options.PrivateDependencies.Add("ShaderCompilerVulkan");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (Sdk.HasValid("PS4Sdk"))
|
||||
options.PrivateDependencies.Add("ShaderCompilerPS4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetFilesToDeploy(List<string> files)
|
||||
{
|
||||
}
|
||||
}
|
||||
461
Source/Engine/ShadersCompilation/ShadersCompilation.cpp
Normal file
461
Source/Engine/ShadersCompilation/ShadersCompilation.cpp
Normal file
@@ -0,0 +1,461 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "ShadersCompilation.h"
|
||||
#include "ShaderCompilationContext.h"
|
||||
#include "ShaderDebugDataExporter.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Parser/ShaderProcessing.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Graphics/Shaders/GPUShader.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Serialization/MemoryReadStream.h"
|
||||
#include "Engine/Content/Asset.h"
|
||||
#if USE_EDITOR
|
||||
#define COMPILE_WITH_ASSETS_IMPORTER 1 // Hack to use shaders importing in this module
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Platform/FileSystemWatcher.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Editor/Editor.h"
|
||||
#endif
|
||||
|
||||
#if COMPILE_WITH_D3D_SHADER_COMPILER
|
||||
#include "DirectX/ShaderCompilerD3D.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_DX_SHADER_COMPILER
|
||||
#include "DirectX/ShaderCompilerDX.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_OGL_SHADER_COMPILER
|
||||
#include "OpenGL/ShaderCompilerOGL.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
#include "Vulkan/ShaderCompilerVulkan.h"
|
||||
#endif
|
||||
#if COMPILE_WITH_PS4_SHADER_COMPILER
|
||||
#include "Platforms/PS4/Engine/ShaderCompilerPS4/ShaderCompilerPS4.h"
|
||||
#endif
|
||||
|
||||
namespace ShadersCompilationImpl
|
||||
{
|
||||
CriticalSection Locker;
|
||||
Array<ShaderCompiler*> Compilers;
|
||||
Array<ShaderCompiler*> ReadyCompilers;
|
||||
}
|
||||
|
||||
using namespace ShadersCompilationImpl;
|
||||
|
||||
class ShadersCompilationService : public EngineService
|
||||
{
|
||||
public:
|
||||
|
||||
ShadersCompilationService()
|
||||
: EngineService(TEXT("Shaders Compilation Service"), -100)
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
void Dispose() override;
|
||||
};
|
||||
|
||||
ShadersCompilationService ShadersCompilationServiceInstance;
|
||||
|
||||
bool ShadersCompilation::Compile(const ShaderCompilationOptions& options)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Shader.Compile");
|
||||
|
||||
// Validate input options
|
||||
if (options.TargetName.IsEmpty() || !options.TargetID.IsValid())
|
||||
{
|
||||
LOG(Warning, "Unknown target object.");
|
||||
return true;
|
||||
}
|
||||
if (options.Output == nullptr)
|
||||
{
|
||||
LOG(Warning, "Missing output.");
|
||||
return true;
|
||||
}
|
||||
if (options.Profile == ShaderProfile::Unknown)
|
||||
{
|
||||
LOG(Warning, "Unknown shader profile.");
|
||||
return true;
|
||||
}
|
||||
if (options.Source == nullptr || options.SourceLength < 1)
|
||||
{
|
||||
LOG(Warning, "Missing source code.");
|
||||
return true;
|
||||
}
|
||||
|
||||
const DateTime startTime = DateTime::NowUTC();
|
||||
const FeatureLevel featureLevel = RenderTools::GetFeatureLevel(options.Profile);
|
||||
|
||||
// Process shader source to collect metadata
|
||||
ShaderMeta meta;
|
||||
if (ShaderProcessing::Parser::Process(options.TargetName, options.Source, options.SourceLength, options.Macros, featureLevel, &meta))
|
||||
{
|
||||
LOG(Warning, "Failed to parse source code.");
|
||||
return true;
|
||||
}
|
||||
const int32 shadersCount = meta.GetShadersCount();
|
||||
if (shadersCount == 0)
|
||||
{
|
||||
LOG(Warning, "Shader has no valid functions.");
|
||||
}
|
||||
|
||||
// Perform actual compilation
|
||||
bool result;
|
||||
{
|
||||
ShaderCompilationContext context(&options, &meta);
|
||||
|
||||
// Request shaders compiler
|
||||
auto compiler = RequestCompiler(options.Profile);
|
||||
if (compiler == nullptr)
|
||||
{
|
||||
LOG(Error, "Shader compiler request failed.");
|
||||
return true;
|
||||
}
|
||||
ASSERT(compiler->GetProfile() == options.Profile);
|
||||
|
||||
// Call compilation process
|
||||
result = compiler->Compile(&context);
|
||||
|
||||
// Dismiss compiler
|
||||
FreeCompiler(compiler);
|
||||
|
||||
#if GPU_USE_SHADERS_DEBUG_LAYER
|
||||
// Export debug data
|
||||
ShaderDebugDataExporter::Export(&context);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Print info if succeed
|
||||
if (result == false)
|
||||
{
|
||||
const DateTime endTime = DateTime::NowUTC();
|
||||
LOG(Info, "Shader compilation '{0}' succeed in {1} ms (profile: {2})", options.TargetName, Math::CeilToInt(static_cast<float>((endTime - startTime).GetTotalMilliseconds())), ::ToString(options.Profile));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderCompiler* ShadersCompilation::CreateCompiler(ShaderProfile profile)
|
||||
{
|
||||
ShaderCompiler* result = nullptr;
|
||||
|
||||
switch (profile)
|
||||
{
|
||||
#if COMPILE_WITH_D3D_SHADER_COMPILER
|
||||
// Direct 3D
|
||||
case ShaderProfile::DirectX_SM4:
|
||||
case ShaderProfile::DirectX_SM5:
|
||||
result = New<ShaderCompilerD3D>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_DX_SHADER_COMPILER
|
||||
case ShaderProfile::DirectX_SM6:
|
||||
result = New<ShaderCompilerDX>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_OGL_SHADER_COMPILER
|
||||
// OpenGL and OpenGL ES
|
||||
case ShaderProfile::GLSL_410:
|
||||
case ShaderProfile::GLSL_440:
|
||||
result = New<ShaderCompilerOGL>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
// Vulkan
|
||||
case ShaderProfile::Vulkan_SM5:
|
||||
result = New<ShaderCompilerVulkan>(profile);
|
||||
break;
|
||||
#endif
|
||||
#if COMPILE_WITH_PS4_SHADER_COMPILER
|
||||
// PS4
|
||||
case ShaderProfile::PS4:
|
||||
result = New<ShaderCompilerPS4>();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ASSERT_LOW_LAYER(result == nullptr || result->GetProfile() == profile);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile)
|
||||
{
|
||||
ShaderCompiler* compiler;
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Try to find ready compiler
|
||||
for (int32 i = 0; i < ReadyCompilers.Count(); i++)
|
||||
{
|
||||
compiler = ReadyCompilers[i];
|
||||
if (compiler->GetProfile() == profile)
|
||||
{
|
||||
// Use it
|
||||
ReadyCompilers.RemoveAt(i);
|
||||
return compiler;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new compiler for a target profile
|
||||
compiler = CreateCompiler(profile);
|
||||
if (compiler == nullptr)
|
||||
{
|
||||
LOG(Error, "Cannot create Shader Compiler for profile {0}", ::ToString(profile));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Register new compiler
|
||||
Compilers.Add(compiler);
|
||||
|
||||
return compiler;
|
||||
}
|
||||
|
||||
void ShadersCompilation::FreeCompiler(ShaderCompiler* compiler)
|
||||
{
|
||||
ScopeLock lock(Locker);
|
||||
ASSERT(compiler && ReadyCompilers.Contains(compiler) == false);
|
||||
|
||||
// Check if service has been disposed (this compiler is not in the compilers list)
|
||||
if (Compilers.Contains(compiler) == false)
|
||||
{
|
||||
// Delete it manually
|
||||
Delete(compiler);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Make compiler free again
|
||||
ReadyCompilers.Add(compiler);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
CriticalSection ShaderIncludesMapLocker;
|
||||
Dictionary<String, Array<Asset*>> ShaderIncludesMap;
|
||||
Dictionary<String, FileSystemWatcher*> ShaderIncludesWatcher;
|
||||
|
||||
void OnShaderIncludesWatcherEvent(const String& path, FileSystemAction action)
|
||||
{
|
||||
if (action == FileSystemAction::Delete)
|
||||
return;
|
||||
|
||||
Array<Asset*> toReload;
|
||||
{
|
||||
ScopeLock lock(ShaderIncludesMapLocker);
|
||||
|
||||
auto file = ShaderIncludesMap.Find(path);
|
||||
if (file == ShaderIncludesMap.End())
|
||||
return;
|
||||
toReload = file->Value;
|
||||
}
|
||||
|
||||
LOG(Info, "Shader include \'{0}\' has been modified.", path);
|
||||
|
||||
// Wait a little so app that was editing the file (e.g. Visual Studio, Notepad++) has enough time to flush whole file change
|
||||
Platform::Sleep(100);
|
||||
|
||||
// Reload shaders using this include
|
||||
for (Asset* asset : toReload)
|
||||
{
|
||||
asset->Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadersCompilation::RegisterForShaderReloads(Asset* asset, const String& includedPath)
|
||||
{
|
||||
ScopeLock lock(ShaderIncludesMapLocker);
|
||||
|
||||
// Add to collection
|
||||
const bool alreadyAdded = ShaderIncludesMap.ContainsKey(includedPath);
|
||||
auto& file = ShaderIncludesMap[includedPath];
|
||||
ASSERT_LOW_LAYER(!file.Contains(asset));
|
||||
file.Add(asset);
|
||||
|
||||
if (!alreadyAdded)
|
||||
{
|
||||
// Create a directory watcher to track the included file changes
|
||||
const String directory = StringUtils::GetDirectoryName(includedPath);
|
||||
if (!ShaderIncludesWatcher.ContainsKey(directory))
|
||||
{
|
||||
auto watcher = New<FileSystemWatcher>(directory, false);
|
||||
watcher->OnEvent.Bind<OnShaderIncludesWatcherEvent>();
|
||||
ShaderIncludesWatcher.Add(directory, watcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShadersCompilation::UnregisterForShaderReloads(Asset* asset)
|
||||
{
|
||||
ScopeLock lock(ShaderIncludesMapLocker);
|
||||
|
||||
// Remove asset reference
|
||||
for (auto& file : ShaderIncludesMap)
|
||||
{
|
||||
file.Value.Remove(asset);
|
||||
}
|
||||
}
|
||||
|
||||
void ShadersCompilation::ExtractShaderIncludes(byte* shaderCache, int32 shaderCacheLength, Array<String>& includes)
|
||||
{
|
||||
MemoryReadStream stream(shaderCache, shaderCacheLength);
|
||||
|
||||
// Read cache format version
|
||||
int32 version;
|
||||
stream.ReadInt32(&version);
|
||||
if (version != GPU_SHADER_CACHE_VERSION)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the location of additional data that contains list of included source files
|
||||
int32 additionalDataStart;
|
||||
stream.ReadInt32(&additionalDataStart);
|
||||
stream.SetPosition(additionalDataStart);
|
||||
|
||||
// Read all includes
|
||||
int32 includesCount;
|
||||
stream.ReadInt32(&includesCount);
|
||||
includes.Clear();
|
||||
for (int32 i = 0; i < includesCount; i++)
|
||||
{
|
||||
String& include = includes.AddOne();
|
||||
stream.ReadString(&include, 11);
|
||||
DateTime lastEditTime;
|
||||
stream.Read(&lastEditTime);
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
namespace
|
||||
{
|
||||
Array<FileSystemWatcher*> ShadersSourcesWatchers;
|
||||
|
||||
// Tries to generate a stable and unique ID for the given shader name.
|
||||
// Used in order to keep the same shader IDs and reduce version control issues with binary diff on ID.
|
||||
Guid GetShaderAssetId(const String& name)
|
||||
{
|
||||
Guid result;
|
||||
result.A = name.Length() * 100;
|
||||
result.B = GetHash(name);
|
||||
result.C = name.HasChars() ? name[0] : 0;
|
||||
result.D = name.HasChars() ? name[name.Length() - 1] : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnWatcherShadersEvent(const String& path, FileSystemAction action)
|
||||
{
|
||||
if (action == FileSystemAction::Delete || !path.EndsWith(TEXT(".shader")))
|
||||
return;
|
||||
|
||||
LOG(Info, "Shader \'{0}\' has been modified.", path);
|
||||
|
||||
// Wait a little so app that was editing the file (e.g. Visual Studio, Notepad++) has enough time to flush whole file change
|
||||
Platform::Sleep(100);
|
||||
|
||||
// Perform hot reload
|
||||
const int32 srcSubDirStart = path.FindLast(TEXT("/Source/Shaders"));
|
||||
if (srcSubDirStart == -1)
|
||||
return;
|
||||
String projectFolderPath = path.Substring(0, srcSubDirStart);
|
||||
FileSystem::NormalizePath(projectFolderPath);
|
||||
const String shadersAssetsPath = projectFolderPath / TEXT("/Content/Shaders");
|
||||
const String shadersSourcePath = projectFolderPath / TEXT("/Source/Shaders");
|
||||
const String localPath = FileSystem::ConvertAbsolutePathToRelative(shadersSourcePath, path);
|
||||
const String name = StringUtils::GetPathWithoutExtension(localPath);
|
||||
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
Guid id = GetShaderAssetId(name);
|
||||
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
|
||||
}
|
||||
|
||||
void RegisterShaderWatchers(const ProjectInfo* project, HashSet<const ProjectInfo*>& projects)
|
||||
{
|
||||
if (projects.Contains(project))
|
||||
return;
|
||||
projects.Add(project);
|
||||
|
||||
// Check if project uses shaders sources
|
||||
const String shadersSourcePath = project->ProjectFolderPath / TEXT("/Source/Shaders");
|
||||
if (FileSystem::DirectoryExists(shadersSourcePath))
|
||||
{
|
||||
// Track engine shaders editing
|
||||
auto sourceWatcher = New<FileSystemWatcher>(shadersSourcePath, true);
|
||||
sourceWatcher->OnEvent.Bind<OnWatcherShadersEvent>();
|
||||
ShadersSourcesWatchers.Add(sourceWatcher);
|
||||
|
||||
// Reimport modified or import added shaders
|
||||
Array<String> files(64);
|
||||
const String shadersAssetsPath = project->ProjectFolderPath / TEXT("/Content/Shaders");
|
||||
FileSystem::DirectoryGetFiles(files, shadersSourcePath, TEXT("*.shader"), DirectorySearchOption::AllDirectories);
|
||||
for (int32 i = 0; i < files.Count(); i++)
|
||||
{
|
||||
const String& path = files[i];
|
||||
const String localPath = FileSystem::ConvertAbsolutePathToRelative(shadersSourcePath, path);
|
||||
const String name = StringUtils::GetPathWithoutExtension(localPath);
|
||||
const String outputPath = shadersAssetsPath / name + ASSET_FILES_EXTENSION_WITH_DOT;
|
||||
Guid id = GetShaderAssetId(name);
|
||||
AssetsImportingManager::ImportIfEdited(path, outputPath, id);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize referenced projects
|
||||
for (const auto& reference : project->References)
|
||||
{
|
||||
if (reference.Project)
|
||||
RegisterShaderWatchers(reference.Project, projects);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool ShadersCompilationService::Init()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
// Initialize automatic shaders importing and reloading for all loaded projects (game, engine, plugins)
|
||||
HashSet<const ProjectInfo*> projects;
|
||||
RegisterShaderWatchers(Editor::Project, projects);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShadersCompilationService::Dispose()
|
||||
{
|
||||
#if USE_EDITOR
|
||||
ShadersSourcesWatchers.ClearDelete();
|
||||
#endif
|
||||
|
||||
Locker.Lock();
|
||||
|
||||
// Check if any compilation is running
|
||||
if (ReadyCompilers.Count() != Compilers.Count())
|
||||
{
|
||||
LOG(Error, "Cannot dispose Shaders Compilation Service. One or more compilers are still in use.");
|
||||
}
|
||||
|
||||
// Cleanup all compilers (delete only those which are not in use)
|
||||
ReadyCompilers.ClearDelete();
|
||||
Compilers.Clear();
|
||||
|
||||
Locker.Unlock();
|
||||
|
||||
// Cleanup shader includes
|
||||
ShaderCompiler::DisposeIncludedFilesCache();
|
||||
|
||||
// Clear includes scanning
|
||||
ShaderIncludesMapLocker.Lock();
|
||||
ShaderIncludesMap.Clear();
|
||||
ShaderIncludesWatcher.ClearDelete();
|
||||
ShaderIncludesMapLocker.Unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
53
Source/Engine/ShadersCompilation/ShadersCompilation.h
Normal file
53
Source/Engine/ShadersCompilation/ShadersCompilation.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompiler.h"
|
||||
|
||||
class Asset;
|
||||
|
||||
/// <summary>
|
||||
/// Shaders compilation service allows to compile shader source code for a desire platform. Supports multi-threading.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API ShadersCompilation
|
||||
{
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Compiles the shader.
|
||||
/// </summary>
|
||||
/// <param name="options">Compilation options</param>
|
||||
/// <returns>True if failed, otherwise false</returns>
|
||||
static bool Compile(const ShaderCompilationOptions& options);
|
||||
|
||||
/// <summary>
|
||||
/// Registers shader asset for the automated reloads on source includes changes.
|
||||
/// </summary
|
||||
/// <param name="asset">The asset.</param>
|
||||
/// <param name="includedPath">The included file path.</param>
|
||||
static void RegisterForShaderReloads(Asset* asset, const String& includedPath);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters shader asset from the automated reloads on source includes changes.
|
||||
/// </summary>
|
||||
/// <param name="asset">The asset.</param>
|
||||
static void UnregisterForShaderReloads(Asset* asset);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the included shader files stored in the shader cache data.
|
||||
/// </summary>
|
||||
/// <param name="shaderCache">The shader cache data.</param>
|
||||
/// <param name="shaderCacheLength">The shader cache data length (in bytes).</param>
|
||||
/// <param name="includes">The output included.</param>
|
||||
static void ExtractShaderIncludes(byte* shaderCache, int32 shaderCacheLength, Array<String>& includes);
|
||||
|
||||
private:
|
||||
|
||||
static ShaderCompiler* CreateCompiler(ShaderProfile profile);
|
||||
static ShaderCompiler* RequestCompiler(ShaderProfile profile);
|
||||
static void FreeCompiler(ShaderCompiler* compiler);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Vulkan shaders compiler module.
|
||||
/// </summary>
|
||||
public class ShaderCompilerVulkan : ShaderCompiler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDefinitions.Add("COMPILE_WITH_VK_SHADER_COMPILER");
|
||||
|
||||
options.PrivateDependencies.Add("glslang");
|
||||
options.PrivateDependencies.Add("spirv-tools");
|
||||
}
|
||||
}
|
||||
814
Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp
Normal file
814
Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp
Normal file
@@ -0,0 +1,814 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
|
||||
#include "ShaderCompilerVulkan.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Platform/Platform.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Serialization/MemoryWriteStream.h"
|
||||
#include "Engine/GraphicsDevice/Vulkan/Types.h"
|
||||
|
||||
// Use glslang for HLSL to SPIR-V translation
|
||||
// Source: https://github.com/KhronosGroup/glslang
|
||||
// License: modified BSD
|
||||
#define NV_EXTENSIONS 1
|
||||
#define AMD_EXTENSIONS 1
|
||||
#define ENABLE_HLSL 1
|
||||
#define ENABLE_OPT 1
|
||||
#include <ThirdParty/glslang/Public/ShaderLang.h>
|
||||
#include <ThirdParty/glslang/MachineIndependent/iomapper.h>
|
||||
#include <ThirdParty/glslang/SPIRV/GlslangToSpv.h>
|
||||
#include <ThirdParty/spirv-tools/libspirv.hpp>
|
||||
|
||||
#define PRINT_UNIFORMS 0
|
||||
#define PRINT_DESCRIPTORS 0
|
||||
|
||||
namespace
|
||||
{
|
||||
CriticalSection CompileShaderVulkanLocker;
|
||||
int32 CompileShaderVulkanInstances = 0;
|
||||
}
|
||||
|
||||
class Includer : public glslang::TShader::Includer
|
||||
{
|
||||
private:
|
||||
|
||||
ShaderCompilationContext* _context;
|
||||
|
||||
IncludeResult* include(const char* headerName, const char* includerName, int depth) const
|
||||
{
|
||||
const char* source;
|
||||
int32 sourceLength;
|
||||
if (ShaderCompiler::GetIncludedFileSource(_context, includerName, headerName, source, sourceLength))
|
||||
return nullptr;
|
||||
return New<IncludeResult>(headerName, source, sourceLength, nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Includer(ShaderCompilationContext* context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [glslang::TShader::Include]
|
||||
IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override
|
||||
{
|
||||
return include(headerName, includerName, (int)inclusionDepth);
|
||||
}
|
||||
|
||||
IncludeResult* includeSystem(const char* headerName, const char* includerName, size_t inclusionDepth) override
|
||||
{
|
||||
return include(headerName, includerName, (int)inclusionDepth);
|
||||
}
|
||||
|
||||
void releaseInclude(IncludeResult* result) override
|
||||
{
|
||||
if (result)
|
||||
Delete(result);
|
||||
}
|
||||
};
|
||||
|
||||
ShaderCompilerVulkan::ShaderCompilerVulkan(ShaderProfile profile)
|
||||
: ShaderCompiler(profile)
|
||||
{
|
||||
ScopeLock lock(CompileShaderVulkanLocker);
|
||||
if (CompileShaderVulkanInstances == 0)
|
||||
{
|
||||
glslang::InitializeProcess();
|
||||
const auto ver = glslang::GetVersion();
|
||||
LOG(Info, "Using glslang {0}.{1}.{2} compiler (SPIR-V version: {3})", ver.major, ver.minor, ver.patch, String(spvSoftwareVersionString()));
|
||||
}
|
||||
CompileShaderVulkanInstances++;
|
||||
}
|
||||
|
||||
ShaderCompilerVulkan::~ShaderCompilerVulkan()
|
||||
{
|
||||
ScopeLock lock(CompileShaderVulkanLocker);
|
||||
CompileShaderVulkanInstances--;
|
||||
if (CompileShaderVulkanInstances == 0)
|
||||
{
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
const TBuiltInResource DefaultTBuiltInResource =
|
||||
{
|
||||
/* .MaxLights = */ 32,
|
||||
/* .MaxClipPlanes = */ 6,
|
||||
/* .MaxTextureUnits = */ 32,
|
||||
/* .MaxTextureCoords = */ 32,
|
||||
/* .MaxVertexAttribs = */ 64,
|
||||
/* .MaxVertexUniformComponents = */ 4096,
|
||||
/* .MaxVaryingFloats = */ 64,
|
||||
/* .MaxVertexTextureImageUnits = */ 32,
|
||||
/* .MaxCombinedTextureImageUnits = */ 80,
|
||||
/* .MaxTextureImageUnits = */ 32,
|
||||
/* .MaxFragmentUniformComponents = */ 4096,
|
||||
/* .MaxDrawBuffers = */ 32,
|
||||
/* .MaxVertexUniformVectors = */ 128,
|
||||
/* .MaxVaryingVectors = */ 8,
|
||||
/* .MaxFragmentUniformVectors = */ 16,
|
||||
/* .MaxVertexOutputVectors = */ 16,
|
||||
/* .MaxFragmentInputVectors = */ 15,
|
||||
/* .MinProgramTexelOffset = */ -8,
|
||||
/* .MaxProgramTexelOffset = */ 7,
|
||||
/* .MaxClipDistances = */ 8,
|
||||
/* .MaxComputeWorkGroupCountX = */ 65535,
|
||||
/* .MaxComputeWorkGroupCountY = */ 65535,
|
||||
/* .MaxComputeWorkGroupCountZ = */ 65535,
|
||||
/* .MaxComputeWorkGroupSizeX = */ 1024,
|
||||
/* .MaxComputeWorkGroupSizeY = */ 1024,
|
||||
/* .MaxComputeWorkGroupSizeZ = */ 64,
|
||||
/* .MaxComputeUniformComponents = */ 1024,
|
||||
/* .MaxComputeTextureImageUnits = */ 16,
|
||||
/* .MaxComputeImageUniforms = */ 8,
|
||||
/* .MaxComputeAtomicCounters = */ 8,
|
||||
/* .MaxComputeAtomicCounterBuffers = */ 1,
|
||||
/* .MaxVaryingComponents = */ 60,
|
||||
/* .MaxVertexOutputComponents = */ 64,
|
||||
/* .MaxGeometryInputComponents = */ 64,
|
||||
/* .MaxGeometryOutputComponents = */ 128,
|
||||
/* .MaxFragmentInputComponents = */ 128,
|
||||
/* .MaxImageUnits = */ 8,
|
||||
/* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
|
||||
/* .MaxCombinedShaderOutputResources = */ 8,
|
||||
/* .MaxImageSamples = */ 0,
|
||||
/* .MaxVertexImageUniforms = */ 0,
|
||||
/* .MaxTessControlImageUniforms = */ 0,
|
||||
/* .MaxTessEvaluationImageUniforms = */ 0,
|
||||
/* .MaxGeometryImageUniforms = */ 0,
|
||||
/* .MaxFragmentImageUniforms = */ 8,
|
||||
/* .MaxCombinedImageUniforms = */ 8,
|
||||
/* .MaxGeometryTextureImageUnits = */ 16,
|
||||
/* .MaxGeometryOutputVertices = */ 256,
|
||||
/* .MaxGeometryTotalOutputComponents = */ 1024,
|
||||
/* .MaxGeometryUniformComponents = */ 1024,
|
||||
/* .MaxGeometryVaryingComponents = */ 64,
|
||||
/* .MaxTessControlInputComponents = */ 128,
|
||||
/* .MaxTessControlOutputComponents = */ 128,
|
||||
/* .MaxTessControlTextureImageUnits = */ 16,
|
||||
/* .MaxTessControlUniformComponents = */ 1024,
|
||||
/* .MaxTessControlTotalOutputComponents = */ 4096,
|
||||
/* .MaxTessEvaluationInputComponents = */ 128,
|
||||
/* .MaxTessEvaluationOutputComponents = */ 128,
|
||||
/* .MaxTessEvaluationTextureImageUnits = */ 16,
|
||||
/* .MaxTessEvaluationUniformComponents = */ 1024,
|
||||
/* .MaxTessPatchComponents = */ 120,
|
||||
/* .MaxPatchVertices = */ 32,
|
||||
/* .MaxTessGenLevel = */ 64,
|
||||
/* .MaxViewports = */ 16,
|
||||
/* .MaxVertexAtomicCounters = */ 0,
|
||||
/* .MaxTessControlAtomicCounters = */ 0,
|
||||
/* .MaxTessEvaluationAtomicCounters = */ 0,
|
||||
/* .MaxGeometryAtomicCounters = */ 0,
|
||||
/* .MaxFragmentAtomicCounters = */ 8,
|
||||
/* .MaxCombinedAtomicCounters = */ 8,
|
||||
/* .MaxAtomicCounterBindings = */ 1,
|
||||
/* .MaxVertexAtomicCounterBuffers = */ 0,
|
||||
/* .MaxTessControlAtomicCounterBuffers = */ 0,
|
||||
/* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
|
||||
/* .MaxGeometryAtomicCounterBuffers = */ 0,
|
||||
/* .MaxFragmentAtomicCounterBuffers = */ 1,
|
||||
/* .MaxCombinedAtomicCounterBuffers = */ 1,
|
||||
/* .MaxAtomicCounterBufferSize = */ 16384,
|
||||
/* .MaxTransformFeedbackBuffers = */ 4,
|
||||
/* .MaxTransformFeedbackInterleavedComponents = */ 64,
|
||||
/* .MaxCullDistances = */ 8,
|
||||
/* .MaxCombinedClipAndCullDistances = */ 8,
|
||||
/* .MaxSamples = */ 4,
|
||||
/* .maxMeshOutputVerticesNV = */ 256,
|
||||
/* .maxMeshOutputPrimitivesNV = */ 512,
|
||||
/* .maxMeshWorkGroupSizeX_NV = */ 32,
|
||||
/* .maxMeshWorkGroupSizeY_NV = */ 1,
|
||||
/* .maxMeshWorkGroupSizeZ_NV = */ 1,
|
||||
/* .maxTaskWorkGroupSizeX_NV = */ 32,
|
||||
/* .maxTaskWorkGroupSizeY_NV = */ 1,
|
||||
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
|
||||
/* .maxMeshViewCountNV = */ 4,
|
||||
/* .maxDualSourceDrawBuffersEXT = */ 1,
|
||||
/* .limits = */ {
|
||||
/* .nonInductiveForLoops = */ 1,
|
||||
/* .whileLoops = */ 1,
|
||||
/* .doWhileLoops = */ 1,
|
||||
/* .generalUniformIndexing = */ 1,
|
||||
/* .generalAttributeMatrixVectorIndexing = */ 1,
|
||||
/* .generalVaryingIndexing = */ 1,
|
||||
/* .generalSamplerIndexing = */ 1,
|
||||
/* .generalVariableIndexing = */ 1,
|
||||
/* .generalConstantMatrixVectorIndexing = */ 1,
|
||||
}
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
struct Descriptor
|
||||
{
|
||||
int32 Slot;
|
||||
int32 Binding;
|
||||
int32 Size;
|
||||
SpirvShaderResourceBindingType BindingType;
|
||||
VkDescriptorType DescriptorType;
|
||||
SpirvShaderResourceType ResourceType;
|
||||
std::string Name;
|
||||
};
|
||||
|
||||
SpirvShaderResourceType GetTextureType(const glslang::TSampler& sampler)
|
||||
{
|
||||
switch (sampler.dim)
|
||||
{
|
||||
case glslang::Esd1D:
|
||||
return sampler.isArrayed() ? SpirvShaderResourceType::Texture1DArray : SpirvShaderResourceType::Texture1D;
|
||||
case glslang::Esd2D:
|
||||
return sampler.isArrayed() ? SpirvShaderResourceType::Texture2DArray : SpirvShaderResourceType::Texture2D;
|
||||
case glslang::Esd3D:
|
||||
return SpirvShaderResourceType::Texture3D;
|
||||
case glslang::EsdCube:
|
||||
return SpirvShaderResourceType::TextureCube;
|
||||
default:
|
||||
CRASH;
|
||||
return SpirvShaderResourceType::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
class DescriptorsCollector
|
||||
{
|
||||
public:
|
||||
|
||||
int32 Images;
|
||||
int32 Buffers;
|
||||
int32 DescriptorsCount;
|
||||
Descriptor Descriptors[SpirvShaderDescriptorInfo::MaxDescriptors];
|
||||
|
||||
public:
|
||||
|
||||
DescriptorsCollector()
|
||||
{
|
||||
Images = 0;
|
||||
Buffers = 0;
|
||||
DescriptorsCount = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Descriptor* Add(glslang::TVarEntryInfo& ent)
|
||||
{
|
||||
const glslang::TType& type = ent.symbol->getType();
|
||||
const char* name = ent.symbol->getName().c_str();
|
||||
auto& qualifier = type.getQualifier();
|
||||
if (DescriptorsCount == SpirvShaderDescriptorInfo::MaxDescriptors)
|
||||
{
|
||||
// Prevent too many descriptors
|
||||
LOG(Warning, "Too many descriptors in use.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Guess the descriptor type based on reflection information
|
||||
VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_MAX_ENUM;
|
||||
SpirvShaderResourceType resourceType = SpirvShaderResourceType::Unknown;
|
||||
SpirvShaderResourceBindingType resourceBindingType = SpirvShaderResourceBindingType::INVALID;
|
||||
if (type.getBasicType() == glslang::EbtSampler)
|
||||
{
|
||||
if (!qualifier.hasBinding())
|
||||
{
|
||||
// Each resource must have binding specified (from HLSL shaders that do it explicitly)
|
||||
LOG(Warning, "Found an uniform \'{0}\' without a binding qualifier. Each uniform must have an explicitly defined binding number.", String(name));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type.getSampler().isCombined())
|
||||
{
|
||||
// Texture + Sampler combined is not supported
|
||||
LOG(Warning, "Combined sampler \'{0}\' from glsl language is not supported.", String(name));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (type.getSampler().isPureSampler())
|
||||
{
|
||||
// Sampler
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
resourceType = SpirvShaderResourceType::Sampler;
|
||||
resourceBindingType = SpirvShaderResourceBindingType::SAMPLER;
|
||||
}
|
||||
else if (type.getSampler().dim == glslang::EsdBuffer)
|
||||
{
|
||||
// Buffer SRV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
resourceType = SpirvShaderResourceType::Buffer;
|
||||
resourceBindingType = SpirvShaderResourceBindingType::SRV;
|
||||
}
|
||||
else if (type.isTexture())
|
||||
{
|
||||
// Texture SRV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
resourceType = GetTextureType(type.getSampler());
|
||||
resourceBindingType = SpirvShaderResourceBindingType::SRV;
|
||||
}
|
||||
else if (type.isImage())
|
||||
{
|
||||
if (type.getSampler().dim == glslang::EsdBuffer)
|
||||
{
|
||||
// Buffer UAV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
resourceType = SpirvShaderResourceType::Buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Texture UAV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
resourceType = GetTextureType(type.getSampler());
|
||||
}
|
||||
resourceBindingType = SpirvShaderResourceBindingType::UAV;
|
||||
}
|
||||
}
|
||||
else if (qualifier.storage == glslang::EvqUniform)
|
||||
{
|
||||
if (type.getBasicType() != glslang::EbtBlock)
|
||||
{
|
||||
// Skip uniforms that are not contained inside structures
|
||||
LOG(Warning, "Invalid uniform \'{1} {0}\'. Shader uniforms that are not constant buffer blocks are not supported.", String(name), String(type.getBasicTypeString().c_str()));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// CB
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
resourceType = SpirvShaderResourceType::ConstantBuffer;
|
||||
name = type.getTypeName().c_str();
|
||||
resourceBindingType = SpirvShaderResourceBindingType::CB;
|
||||
}
|
||||
else if (qualifier.storage == glslang::EvqBuffer)
|
||||
{
|
||||
if (qualifier.isReadOnly())
|
||||
{
|
||||
// Buffer SRV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
resourceType = SpirvShaderResourceType::Buffer;
|
||||
resourceBindingType = SpirvShaderResourceBindingType::SRV;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer UAV
|
||||
descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
resourceType = SpirvShaderResourceType::Buffer;
|
||||
resourceBindingType = SpirvShaderResourceBindingType::UAV;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the output info about shader uniforms usage
|
||||
switch (descriptorType)
|
||||
{
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
Images++;
|
||||
break;
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
Buffers++;
|
||||
break;
|
||||
default:
|
||||
LOG(Warning, "Invalid descriptor type {0} for symbol {1}.", (int32)descriptorType, String(name));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto index = DescriptorsCount++;
|
||||
auto& descriptor = Descriptors[index];
|
||||
descriptor.Binding = index;
|
||||
descriptor.Slot = qualifier.layoutBinding;
|
||||
descriptor.Size = -1;
|
||||
descriptor.BindingType = resourceBindingType;
|
||||
descriptor.DescriptorType = descriptorType;
|
||||
descriptor.ResourceType = resourceType;
|
||||
descriptor.Name = name;
|
||||
return &descriptor;
|
||||
}
|
||||
};
|
||||
|
||||
class MyIoMapResolver : public glslang::TDefaultIoResolverBase
|
||||
{
|
||||
private:
|
||||
|
||||
int32 _set;
|
||||
DescriptorsCollector* _collector;
|
||||
|
||||
public:
|
||||
|
||||
MyIoMapResolver(int32 set, DescriptorsCollector* collector, const glslang::TIntermediate& intermediate)
|
||||
: TDefaultIoResolverBase(intermediate)
|
||||
, _set(set)
|
||||
, _collector(collector)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
// [glslang::TDefaultIoResolverBase]
|
||||
bool validateBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
glslang::TResourceType getResourceType(const glslang::TType& type) override
|
||||
{
|
||||
if (isUavType(type))
|
||||
return glslang::EResUav;
|
||||
if (isSrvType(type))
|
||||
return glslang::EResTexture;
|
||||
if (isSamplerType(type))
|
||||
return glslang::EResSampler;
|
||||
if (isUboType(type))
|
||||
return glslang::EResUbo;
|
||||
return glslang::EResCount;
|
||||
}
|
||||
|
||||
int resolveBinding(EShLanguage stage, glslang::TVarEntryInfo& ent) override
|
||||
{
|
||||
// Skip unused things
|
||||
if (!ent.live)
|
||||
return -1;
|
||||
|
||||
// Add resource
|
||||
const auto descriptor = _collector->Add(ent);
|
||||
if (descriptor)
|
||||
return ent.newBinding = reserveSlot(_set, descriptor->Binding);
|
||||
return ent.newBinding;
|
||||
}
|
||||
|
||||
int resolveSet(EShLanguage stage, glslang::TVarEntryInfo& ent) override
|
||||
{
|
||||
// Skip unused things
|
||||
if (!ent.live)
|
||||
return -1;
|
||||
|
||||
// Use different slot per-stage
|
||||
return ent.newSet = _set;
|
||||
}
|
||||
};
|
||||
|
||||
bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite)
|
||||
{
|
||||
// TODO: test without locking
|
||||
ScopeLock lock(CompileShaderVulkanLocker);
|
||||
Includer includer(_context);
|
||||
|
||||
// Prepare
|
||||
if (WriteShaderFunctionBegin(_context, meta))
|
||||
return true;
|
||||
auto options = _context->Options;
|
||||
auto type = meta.GetStage();
|
||||
|
||||
// Prepare
|
||||
EShLanguage lang = EShLanguage::EShLangCount;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
lang = EShLanguage::EShLangVertex;
|
||||
break;
|
||||
case ShaderStage::Hull:
|
||||
lang = EShLanguage::EShLangTessControl;
|
||||
break;
|
||||
case ShaderStage::Domain:
|
||||
lang = EShLanguage::EShLangTessEvaluation;
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
lang = EShLanguage::EShLangGeometry;
|
||||
break;
|
||||
case ShaderStage::Pixel:
|
||||
lang = EShLanguage::EShLangFragment;
|
||||
break;
|
||||
case ShaderStage::Compute:
|
||||
lang = EShLanguage::EShLangCompute;
|
||||
break;
|
||||
default:
|
||||
LOG(Error, "Unknown shader type.");
|
||||
return true;
|
||||
}
|
||||
EShMessages messages = (EShMessages)(EShMsgReadHlsl | EShMsgSpvRules | EShMsgVulkanRules);
|
||||
|
||||
// Compile all shader function permutations
|
||||
for (int32 permutationIndex = 0; permutationIndex < meta.Permutations.Count(); permutationIndex++)
|
||||
{
|
||||
#if PRINT_DESCRIPTORS
|
||||
LOG(Warning, "VULKAN SHADER {0}: {1}[{2}]", _context->Options->TargetName, String(meta.Name), permutationIndex);
|
||||
#endif
|
||||
_macros.Clear();
|
||||
|
||||
// Get function permutation macros
|
||||
meta.GetDefinitionsForPermutation(permutationIndex, _macros);
|
||||
|
||||
// Add additional define for compiled function name
|
||||
GetDefineForFunction(meta, _macros);
|
||||
|
||||
// Add custom and global macros (global last because contain null define to indicate ending)
|
||||
_macros.Add(_context->Options->Macros);
|
||||
_macros.Add(_globalMacros);
|
||||
|
||||
// Offset inputs for some pipeline stages to match the descriptors sets layout
|
||||
int32 stageSet;
|
||||
switch (type)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
case ShaderStage::Compute:
|
||||
stageSet = 0;
|
||||
break;
|
||||
case ShaderStage::Pixel:
|
||||
stageSet = 1;
|
||||
break;
|
||||
case ShaderStage::Geometry:
|
||||
stageSet = 2;
|
||||
break;
|
||||
case ShaderStage::Hull:
|
||||
stageSet = 3;
|
||||
break;
|
||||
case ShaderStage::Domain:
|
||||
stageSet = 4;
|
||||
break;
|
||||
default:
|
||||
LOG(Error, "Unknown shader type.");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse HLSL shader using glslang
|
||||
glslang::TShader shader(lang);
|
||||
glslang::TProgram program;
|
||||
shader.setEntryPoint(meta.Name.Get());
|
||||
shader.setSourceEntryPoint(meta.Name.Get());
|
||||
int lengths = options->SourceLength - 1;
|
||||
const char* names = _context->TargetNameAnsi;
|
||||
shader.setStringsWithLengthsAndNames(&options->Source, &lengths, &names, 1);
|
||||
const int defaultVersion = 450;
|
||||
std::string preamble;
|
||||
for (int32 i = 0; i < _macros.Count() - 1; i++)
|
||||
{
|
||||
auto& macro = _macros[i];
|
||||
preamble.append("#define ");
|
||||
preamble.append(macro.Name);
|
||||
if (macro.Definition)
|
||||
{
|
||||
preamble.append(" ");
|
||||
preamble.append(macro.Definition);
|
||||
}
|
||||
preamble.append("\n");
|
||||
}
|
||||
shader.setPreamble(preamble.c_str());
|
||||
shader.setInvertY(true);
|
||||
//shader.setAutoMapLocations(true);
|
||||
//shader.setAutoMapBindings(true);
|
||||
//shader.setShiftBinding(glslang::TResourceType::EResUav, 500);
|
||||
shader.setHlslIoMapping(true);
|
||||
shader.setEnvInput(glslang::EShSourceHlsl, lang, glslang::EShClientVulkan, defaultVersion);
|
||||
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0);
|
||||
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0);
|
||||
if (!shader.parse(&DefaultTBuiltInResource, defaultVersion, false, messages, includer))
|
||||
{
|
||||
const auto msg = shader.getInfoLog();
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
program.addShader(&shader);
|
||||
|
||||
// Generate reflection information
|
||||
if (!program.link(messages))
|
||||
{
|
||||
const auto msg = program.getInfoLog();
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
if (!program.getIntermediate(lang))
|
||||
{
|
||||
const auto msg = program.getInfoLog();
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
DescriptorsCollector descriptorsCollector;
|
||||
MyIoMapResolver resolver(stageSet, &descriptorsCollector, *program.getIntermediate(lang));
|
||||
if (!program.mapIO(&resolver))
|
||||
{
|
||||
const auto msg = program.getInfoLog();
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
if (!program.buildReflection())
|
||||
{
|
||||
const auto msg = program.getInfoLog();
|
||||
_context->OnError(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Process shader reflection data
|
||||
SpirvShaderHeader header;
|
||||
Platform::MemoryClear(&header, sizeof(header));
|
||||
ShaderBindings bindings = { 0, 0, 0, 0 };
|
||||
{
|
||||
// Extract constant buffers usage information
|
||||
for (int blockIndex = 0; blockIndex < program.getNumLiveUniformBlocks(); blockIndex++)
|
||||
{
|
||||
auto size = program.getUniformBlockSize(blockIndex);
|
||||
auto uniform = program.getUniformBlockTType(blockIndex);
|
||||
auto& qualifier = uniform->getQualifier();
|
||||
auto binding = (int32)qualifier.layoutBinding;
|
||||
|
||||
if (!qualifier.hasBinding())
|
||||
{
|
||||
// Each uniform must have a valid binding
|
||||
//LOG(Warning, "Found a uniform block \'{0}\' without a binding qualifier. Each uniform block must have an explicitly defined binding number.", String(uniform->getTypeName().c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Shared storage buffer
|
||||
if (qualifier.storage == glslang::EvqBuffer)
|
||||
{
|
||||
// RWBuffer
|
||||
}
|
||||
else
|
||||
{
|
||||
// Uniform buffer
|
||||
bool found = false;
|
||||
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
|
||||
{
|
||||
auto& descriptor = descriptorsCollector.Descriptors[i];
|
||||
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB && descriptor.Binding == binding)
|
||||
{
|
||||
found = true;
|
||||
descriptor.Size = size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
LOG(Warning, "Failed to find descriptor for the uniform block \'{0}\' of size {1} (bytes), binding: {2}.", String(uniform->getTypeName().c_str()), size, binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if PRINT_UNIFORMS
|
||||
// Debug printing all uniforms
|
||||
for (int32 index = 0; index < program.getNumLiveUniformVariables(); index++)
|
||||
{
|
||||
auto uniform = program.getUniformTType(index);
|
||||
auto qualifier = uniform->getQualifier();
|
||||
if (!uniform->isArray())
|
||||
LOG(Warning, "Shader {0}:{1} - uniform: {2} {3} at binding {4}",
|
||||
_context->TargetNameAnsi,
|
||||
String(meta.Name),
|
||||
uniform->getCompleteString().c_str(),
|
||||
program.getUniformName(index),
|
||||
qualifier.layoutBinding
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Process all descriptors
|
||||
header.DescriptorInfo.ImageInfosCount = descriptorsCollector.Images;
|
||||
header.DescriptorInfo.BufferInfosCount = descriptorsCollector.Buffers;
|
||||
for (int32 i = 0; i < descriptorsCollector.DescriptorsCount; i++)
|
||||
{
|
||||
auto& descriptor = descriptorsCollector.Descriptors[i];
|
||||
|
||||
auto& d = header.DescriptorInfo.DescriptorTypes[header.DescriptorInfo.DescriptorTypesCount++];
|
||||
d.Binding = descriptor.Binding;
|
||||
d.Set = stageSet;
|
||||
d.Slot = descriptor.Slot;
|
||||
d.BindingType = descriptor.BindingType;
|
||||
d.DescriptorType = descriptor.DescriptorType;
|
||||
d.ResourceType = descriptor.ResourceType;
|
||||
|
||||
switch (descriptor.BindingType)
|
||||
{
|
||||
case SpirvShaderResourceBindingType::CB:
|
||||
bindings.UsedCBsMask |= 1 << descriptor.Slot;
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::SRV:
|
||||
bindings.UsedSRsMask |= 1 << descriptor.Slot;
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::UAV:
|
||||
bindings.UsedUAsMask |= 1 << descriptor.Slot;
|
||||
break;
|
||||
}
|
||||
|
||||
if (descriptor.BindingType == SpirvShaderResourceBindingType::CB)
|
||||
{
|
||||
if (descriptor.Size == -1)
|
||||
{
|
||||
// Skip unused constant buffers
|
||||
continue;
|
||||
}
|
||||
if (descriptor.Size == 0)
|
||||
{
|
||||
LOG(Warning, "Found constant buffer \'{1}\' at slot {0} but it's not used or has no valid size.", descriptor.Slot, String(descriptor.Name.c_str()));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32 b = 0; b < _constantBuffers.Count(); b++)
|
||||
{
|
||||
auto& cc = _constantBuffers[b];
|
||||
if (cc.Slot == descriptor.Slot)
|
||||
{
|
||||
// Mark as used and cache some data
|
||||
cc.IsUsed = true;
|
||||
cc.Size = descriptor.Size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if PRINT_DESCRIPTORS
|
||||
String type;
|
||||
switch (descriptor.BindingType)
|
||||
{
|
||||
case SpirvShaderResourceBindingType::INVALID:
|
||||
type = TEXT("INVALID");
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::CB:
|
||||
type = TEXT("CB");
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::SAMPLER:
|
||||
type = TEXT("SAMPLER");
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::SRV:
|
||||
type = TEXT("SRV");
|
||||
break;
|
||||
case SpirvShaderResourceBindingType::UAV:
|
||||
type = TEXT("UAV");
|
||||
break;
|
||||
default:
|
||||
type = TEXT("?");
|
||||
}
|
||||
LOG(Warning, "VULKAN SHADER RESOURCE: slot: {1}, binding: {2}, name: {0}, type: {3}", String(descriptor.Name.c_str()), descriptor.Slot, descriptor.Binding, type);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Generate SPIR-V (optimize it at the same time)
|
||||
std::vector<unsigned> spirv;
|
||||
spv::SpvBuildLogger logger;
|
||||
glslang::SpvOptions spvOptions;
|
||||
spvOptions.generateDebugInfo = false;
|
||||
spvOptions.disassemble = false;
|
||||
spvOptions.disableOptimizer = options->NoOptimize;
|
||||
spvOptions.optimizeSize = !options->NoOptimize;
|
||||
spvOptions.stripDebugInfo = !options->GenerateDebugData;
|
||||
#if BUILD_DEBUG
|
||||
spvOptions.validate = true;
|
||||
#else
|
||||
spvOptions.validate = false;
|
||||
#endif
|
||||
glslang::GlslangToSpv(*program.getIntermediate(lang), spirv, &logger, &spvOptions);
|
||||
const std::string spirvLogOutput = logger.getAllMessages();
|
||||
if (!spirvLogOutput.empty())
|
||||
{
|
||||
LOG(Warning, "SPIR-V generator log:\n{0}", String(spirvLogOutput.c_str()));
|
||||
}
|
||||
if (spirv.empty())
|
||||
{
|
||||
LOG(Warning, "SPIR-V generator failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Dump SPIR-V as text for debugging
|
||||
{
|
||||
spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_0);
|
||||
std::string spirvText;
|
||||
tools.Disassemble(spirv, &spirvText);
|
||||
_context->OnCollectDebugInfo(meta, permutationIndex, spirvText.c_str(), (int32)spirvText.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
int32 spirvBytesCount = (int32)spirv.size() * sizeof(unsigned);
|
||||
header.Type = SpirvShaderHeader::Types::Raw;
|
||||
Array<byte> data;
|
||||
data.Resize(sizeof(header) + spirvBytesCount);
|
||||
Platform::MemoryCopy(data.Get(), &header, sizeof(header));
|
||||
Platform::MemoryCopy(data.Get() + sizeof(header), &spirv[0], spirvBytesCount);
|
||||
|
||||
if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, data.Get(), data.Count()))
|
||||
return true;
|
||||
|
||||
if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros))
|
||||
return true;
|
||||
}
|
||||
|
||||
return WriteShaderFunctionEnd(_context, meta);
|
||||
}
|
||||
|
||||
bool ShaderCompilerVulkan::OnCompileBegin()
|
||||
{
|
||||
if (ShaderCompiler::OnCompileBegin())
|
||||
return true;
|
||||
|
||||
//_globalMacros.Add({ "VULKAN", "1" }); // glslang compiler adds VULKAN define if EShMsgVulkanRules flag is specified
|
||||
|
||||
// TODO: handle options->TreatWarningsAsErrors
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if COMPILE_WITH_VK_SHADER_COMPILER
|
||||
|
||||
#include "Engine/ShadersCompilation/ShaderCompiler.h"
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of shaders compiler for Vulkan rendering backend.
|
||||
/// </summary>
|
||||
class ShaderCompilerVulkan : public ShaderCompiler
|
||||
{
|
||||
private:
|
||||
|
||||
Array<char> _funcNameDefineBuffer;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShaderCompilerVulkan"/> class.
|
||||
/// </summary>
|
||||
/// <param name="profile">The profile.</param>
|
||||
ShaderCompilerVulkan(ShaderProfile profile);
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="ShaderCompilerVulkan"/> class.
|
||||
/// </summary>
|
||||
~ShaderCompilerVulkan();
|
||||
|
||||
protected:
|
||||
|
||||
// [ShaderCompiler]
|
||||
bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override;
|
||||
bool OnCompileBegin() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user