Finished first version of shader compiler. HLSL -> DXIL.
Submitting vertex and frag shader needed to display a triangle.
This commit is contained in:
BIN
Assets/compiled/SolidColor.frag.dxil
Normal file
BIN
Assets/compiled/SolidColor.frag.dxil
Normal file
Binary file not shown.
BIN
Assets/compiled/Triangle.vert.dxil
Normal file
BIN
Assets/compiled/Triangle.vert.dxil
Normal file
Binary file not shown.
4
Assets/source/SolidColor.frag.hlsl
Normal file
4
Assets/source/SolidColor.frag.hlsl
Normal file
@@ -0,0 +1,4 @@
|
||||
float4 main(float4 Color : TEXCOORD0) : SV_Target0
|
||||
{
|
||||
return Color;
|
||||
}
|
||||
39
Assets/source/Triangle.vert.hlsl
Normal file
39
Assets/source/Triangle.vert.hlsl
Normal file
@@ -0,0 +1,39 @@
|
||||
struct Input
|
||||
{
|
||||
uint VertexIndex : SV_VertexID;
|
||||
};
|
||||
|
||||
struct Output
|
||||
{
|
||||
float4 Color : TEXCOORD0;
|
||||
float4 Position : SV_Position;
|
||||
};
|
||||
|
||||
Output main(Input input)
|
||||
{
|
||||
Output output;
|
||||
float2 pos;
|
||||
if (input.VertexIndex == 0)
|
||||
{
|
||||
pos = (-1.0f).xx;
|
||||
output.Color = float4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input.VertexIndex == 1)
|
||||
{
|
||||
pos = float2(1.0f, -1.0f);
|
||||
output.Color = float4(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (input.VertexIndex == 2)
|
||||
{
|
||||
pos = float2(0.0f, 1.0f);
|
||||
output.Color = float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
output.Position = float4(pos, 0.0f, 1.0f);
|
||||
return output;
|
||||
}
|
||||
@@ -20,7 +20,7 @@ using size_t = std::size_t;
|
||||
|
||||
struct ByteBuffer
|
||||
{
|
||||
const Byte* Data;
|
||||
Byte* Data;
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <Core/Common/CoreTypes.h>
|
||||
#include <Juliet.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Juliet
|
||||
{
|
||||
|
||||
@@ -24,8 +25,6 @@ namespace Juliet
|
||||
|
||||
extern void JULIET_API JulietAssert(const char* expression);
|
||||
|
||||
#define DEFER(cleanup) for (bool done_ = 0; !done_; (cleanup), done_ = 1)
|
||||
|
||||
#define ZeroStruct(structInstance) ZeroSize(sizeof(structInstance), &(structInstance))
|
||||
#define ZeroArray(array) ZeroSize(sizeof((array)), (array))
|
||||
#define ZeroDynArray(Count, Pointer) ZeroSize((Count) * sizeof((Pointer)[0]), Pointer)
|
||||
@@ -38,8 +37,44 @@ namespace Juliet
|
||||
}
|
||||
}
|
||||
|
||||
#define MemCopy memcpy
|
||||
template <class Function>
|
||||
class DeferredFunction
|
||||
{
|
||||
public:
|
||||
explicit DeferredFunction(const Function& otherFct) noexcept
|
||||
: Callback(otherFct)
|
||||
{
|
||||
}
|
||||
explicit DeferredFunction(Function&& otherFct) noexcept
|
||||
: Callback(std::move(otherFct))
|
||||
{
|
||||
}
|
||||
|
||||
~DeferredFunction() noexcept { Callback(); }
|
||||
|
||||
DeferredFunction(const DeferredFunction&) = delete;
|
||||
DeferredFunction(const DeferredFunction&&) = delete;
|
||||
void operator=(const DeferredFunction&) = delete;
|
||||
void operator=(DeferredFunction&&) = delete;
|
||||
|
||||
private:
|
||||
Function Callback;
|
||||
};
|
||||
|
||||
template <class Function>
|
||||
auto Defer(Function&& fct) noexcept
|
||||
{
|
||||
return DeferredFunction<std::decay_t<Function>>{ std::forward<Function>(fct) };
|
||||
}
|
||||
|
||||
inline bool IsValid(ByteBuffer buffer)
|
||||
{
|
||||
return buffer.Size > 0 && buffer.Data;
|
||||
}
|
||||
|
||||
extern JULIET_API void Free(ByteBuffer& buffer);
|
||||
|
||||
// TODO move to math utils
|
||||
template <typename Type>
|
||||
constexpr Type Min(Type lhs, Type rhs)
|
||||
{
|
||||
|
||||
@@ -33,6 +33,13 @@ namespace Juliet
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
struct StringBuffer : String
|
||||
{
|
||||
size_t Capacity;
|
||||
};
|
||||
|
||||
constexpr uint32 kInvalidUTF8 = 0xFFFD;
|
||||
|
||||
inline size_t StringLength(String str)
|
||||
{
|
||||
return str.Size;
|
||||
@@ -56,7 +63,7 @@ namespace Juliet
|
||||
return length;
|
||||
}
|
||||
|
||||
inline bool StringIsValid(String str)
|
||||
inline bool IsValid(String str)
|
||||
{
|
||||
return str.Size > 0 && str.Data != nullptr && *str.Data;
|
||||
}
|
||||
@@ -89,7 +96,7 @@ namespace Juliet
|
||||
|
||||
inline bool ContainsChar(String str, char c)
|
||||
{
|
||||
return StringIsValid(FindChar(str, c));
|
||||
return IsValid(FindChar(str, c));
|
||||
}
|
||||
|
||||
// Return:
|
||||
@@ -117,6 +124,9 @@ namespace Juliet
|
||||
return result;
|
||||
}
|
||||
|
||||
JULIET_API uint32 StepUTF8(String& inStr);
|
||||
JULIET_API String FindString(String strLeft, String strRight);
|
||||
|
||||
// Case insensitive compare. Supports ASCII only
|
||||
// TODO: Support UNICODE
|
||||
extern JULIET_API int8 StringCompareCaseInsensitive(String str1, String str2);
|
||||
@@ -127,10 +137,8 @@ namespace Juliet
|
||||
// src and dst will be casted based on the encoding.
|
||||
// size will correspond to the number of characters
|
||||
// Will convert \0 character if present.
|
||||
extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, const char* src, size_t srcLen,
|
||||
char*& dst, size_t& dstLen, size_t dstCapacity);
|
||||
extern JULIET_API bool ConvertString(String from, String to, const char* src, size_t srcLen, char*& dst,
|
||||
size_t& dstLen, size_t dstCapacity);
|
||||
extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst);
|
||||
extern JULIET_API bool ConvertString(String from, String to, String src, StringBuffer& dst);
|
||||
|
||||
} // namespace Juliet
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Juliet
|
||||
|
||||
int64 (*Seek)(NonNullPtr<IOStreamDataPayload> data, int64 offset, IOStreamSeekPivot pivot);
|
||||
size_t (*Read)(NonNullPtr<IOStreamDataPayload> data, void* outBuffer, size_t size, NonNullPtr<IOStreamStatus> status);
|
||||
size_t (*Write)(NonNullPtr<IOStreamDataPayload> data, const void* inBuffer, size_t size, NonNullPtr<IOStreamStatus> status);
|
||||
size_t (*Write)(NonNullPtr<IOStreamDataPayload> data, ByteBuffer inBuffer, NonNullPtr<IOStreamStatus> status);
|
||||
bool (*Flush)(NonNullPtr<IOStreamDataPayload> data, NonNullPtr<IOStreamStatus> status);
|
||||
|
||||
bool (*Close)(NonNullPtr<IOStreamDataPayload> data);
|
||||
@@ -54,7 +54,7 @@ namespace Juliet
|
||||
|
||||
// Write formatted string into the stream.
|
||||
extern JULIET_API size_t IOPrintf(NonNullPtr<IOStream> stream, _Printf_format_string_ const char* format, ...);
|
||||
extern JULIET_API size_t IOWrite(NonNullPtr<IOStream> stream, const void* ptr, size_t size);
|
||||
extern JULIET_API size_t IOWrite(NonNullPtr<IOStream> stream, ByteBuffer inBuffer);
|
||||
|
||||
extern JULIET_API size_t IORead(NonNullPtr<IOStream> stream, void* ptr, size_t size);
|
||||
extern JULIET_API int64 IOSeek(NonNullPtr<IOStream> stream, int64 offset, IOStreamSeekPivot pivot);
|
||||
@@ -62,8 +62,8 @@ namespace Juliet
|
||||
extern JULIET_API int64 IOSize(NonNullPtr<IOStream> stream);
|
||||
|
||||
// TODO : Use memory arena because that Allocates
|
||||
extern JULIET_API uint8* LoadFile(String filename, size_t& outByteRead);
|
||||
extern JULIET_API uint8* LoadFile(NonNullPtr<IOStream> stream, size_t& outByteRead, bool closeStreamWhenDone);
|
||||
extern JULIET_API ByteBuffer LoadFile(String filename);
|
||||
extern JULIET_API ByteBuffer LoadFile(NonNullPtr<IOStream> stream, bool closeStreamWhenDone);
|
||||
|
||||
extern JULIET_API bool IOClose(NonNullPtr<IOStream> stream);
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Juliet
|
||||
std::deque<Entry> Entries;
|
||||
bool IsInitialized : 1;
|
||||
|
||||
friend void Log(LogLevel level, LogCategory category, const char* fmt, ...);
|
||||
friend void Log(LogLevel level, LogCategory category, const char* fmt, va_list args);
|
||||
void AddEntry(Entry&& entry);
|
||||
static void OutputLog(Entry& entry);
|
||||
};
|
||||
@@ -43,4 +43,7 @@ namespace Juliet
|
||||
extern void JULIET_API InitializeLogManager();
|
||||
extern void JULIET_API ShutdownLogManager();
|
||||
extern void JULIET_API Log(LogLevel level, LogCategory category, const char* fmt, ...);
|
||||
extern void JULIET_API LogMessage(LogCategory category, const char* fmt, ...);
|
||||
extern void JULIET_API LogWarning(LogCategory category, const char* fmt, ...);
|
||||
extern void JULIET_API LogError(LogCategory category, const char* fmt, ...);
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Juliet
|
||||
Graphics = 1,
|
||||
Networking = 2,
|
||||
Engine = 3,
|
||||
Editor = 4,
|
||||
Tool = 4,
|
||||
Game = 5,
|
||||
};
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <Juliet.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace Juliet
|
||||
{
|
||||
@@ -15,7 +17,7 @@ namespace Juliet
|
||||
void Free(Type* memory)
|
||||
{
|
||||
Assert(memory);
|
||||
free(memory);
|
||||
::free(memory);
|
||||
}
|
||||
// Free and Set the ptr to nullptr
|
||||
template <typename Type>
|
||||
@@ -23,7 +25,7 @@ namespace Juliet
|
||||
{
|
||||
if (memory)
|
||||
{
|
||||
free(memory);
|
||||
::free(memory);
|
||||
memory = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,5 @@ namespace Juliet
|
||||
}
|
||||
|
||||
#define MemSet memset
|
||||
#define MemCopy memcpy
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Juliet
|
||||
|
||||
// Pack
|
||||
NetworkPacket& operator<<(uint32 value);
|
||||
NetworkPacket& operator<<(const char* data);
|
||||
NetworkPacket& operator<<(char* data);
|
||||
|
||||
protected:
|
||||
void Append(ByteBuffer buffer);
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
#include <pch.h>
|
||||
|
||||
#include <Core/Memory/Allocator.h>
|
||||
|
||||
namespace Juliet
|
||||
{
|
||||
void JulietAssert(const char* expression)
|
||||
{
|
||||
Juliet::Log(Juliet::LogLevel::Error, Juliet::LogCategory::Core, expression);
|
||||
Log(LogLevel::Error, LogCategory::Core, expression);
|
||||
__debugbreak();
|
||||
}
|
||||
|
||||
void Free(ByteBuffer& buffer)
|
||||
{
|
||||
if (buffer.Data)
|
||||
{
|
||||
Free(buffer.Data);
|
||||
}
|
||||
buffer = {};
|
||||
}
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -7,7 +7,6 @@ namespace Juliet
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr char kUnknown_ASCII = '?';
|
||||
constexpr int32 kUnknown_UNICODE = 0xFFFD;
|
||||
|
||||
struct
|
||||
@@ -57,52 +56,162 @@ namespace Juliet
|
||||
*to = from;
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Step(String& inStr, size_t byteToStep)
|
||||
uint32 StepUTF8(String& inStr)
|
||||
{
|
||||
// From rfc3629, the UTF-8 spec:
|
||||
// https://www.ietf.org/rfc/rfc3629.txt
|
||||
//
|
||||
// Char. number range | UTF-8 octet sequence
|
||||
// (hexadecimal) | (binary)
|
||||
// --------------------+---------------------------------------------
|
||||
// 0000 0000-0000 007F | 0xxxxxxx
|
||||
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
//
|
||||
// The function checks character validity and overlong (using too many bytes to encode the character) and return invalid utf8 char if detected.
|
||||
|
||||
// If string is empty then it's done.
|
||||
if (inStr.Size == 0)
|
||||
{
|
||||
Assert(inStr.Size >= byteToStep);
|
||||
|
||||
inStr.Data += byteToStep;
|
||||
inStr.Size -= byteToStep;
|
||||
}
|
||||
|
||||
uint32 StepUTF8(String& inStr, size_t byteToRead)
|
||||
{
|
||||
/*
|
||||
* From rfc3629, the UTF-8 spec:
|
||||
* https://www.ietf.org/rfc/rfc3629.txt
|
||||
*
|
||||
* Char. number range | UTF-8 octet sequence
|
||||
* (hexadecimal) | (binary)
|
||||
* --------------------+---------------------------------------------
|
||||
* 0000 0000-0000 007F | 0xxxxxxx
|
||||
* 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
* 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
* 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
*/
|
||||
|
||||
// If string is empty then it's done.
|
||||
if (inStr.Size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto str = reinterpret_cast<const uint8*>(CStr(inStr));
|
||||
const uint32 octet = byteToRead ? *str : 0;
|
||||
|
||||
if (octet == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((octet & 0x80) == 0x0) // One byte code point
|
||||
{
|
||||
Step(inStr, 1);
|
||||
return octet;
|
||||
}
|
||||
Assert(false && "StepUTF8 only supports one byte codepoints for now");
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
auto str = reinterpret_cast<const uint8*>(CStr(inStr));
|
||||
const uint32 octet = *str;
|
||||
bool isOverlong = false;
|
||||
bool isInvalid = false;
|
||||
bool isUTF16Surrogate = false;
|
||||
if (octet == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((octet & 0x80) == 0x0) // One byte code point: 0xxxxxxx
|
||||
{
|
||||
inStr.Data += 1;
|
||||
inStr.Size -= 1;
|
||||
return octet;
|
||||
}
|
||||
else if ((octet & 0xE0) == 0xC0) // Two bytes code point: 110xxxxx 10xxxxxx
|
||||
{
|
||||
const uint8 secondByte = str[1];
|
||||
if ((secondByte & 0xC0) == 0x80) // Make sure the trailing byte is correct
|
||||
{
|
||||
const uint32 result = ((octet & 0x1F) << 6) | (secondByte & 0x3F);
|
||||
if (result >= 0x80) // If the result is smaller than 0x80 its an overlong!
|
||||
{
|
||||
inStr.Data += 2;
|
||||
inStr.Size -= 1;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
isOverlong = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
else if (((octet & 0xF0) == 0xE0)) // Three bytes code point: 1110xxxx 10xxxxxx 10xxxxxx
|
||||
{
|
||||
const uint8 secondByte = str[1];
|
||||
const uint8 thirdByte = str[2];
|
||||
if (((secondByte & 0xC0) == 0x80) && ((thirdByte & 0xC0) == 0x80)) // Make sure the trailing bytes are correct
|
||||
{
|
||||
const uint32 secondOctet = static_cast<uint32>(secondByte & 0x3F) << 6;
|
||||
const uint32 thirdOctet = static_cast<uint32>(thirdByte & 0x3F);
|
||||
const uint32 result = ((octet & 0x0F) << 12) | secondOctet | thirdOctet;
|
||||
if (result >= 0x0800) // if the result is smaller its an overlong.
|
||||
{
|
||||
if ((result < 0xD800) || (result > 0xDFFF)) // If out of range its an UTF-16 surrogate
|
||||
{
|
||||
inStr.Data += 3;
|
||||
inStr.Size -= 1;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
isUTF16Surrogate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isOverlong = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
else if (((octet & 0xF8) == 0xF0))
|
||||
{ // Four bytes code point: 11110xxxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
const uint8 secondByte = str[1];
|
||||
const uint8 thirdByte = str[2];
|
||||
const uint8 fourthByte = str[3];
|
||||
if (((secondByte & 0xC0) == 0x80) && ((thirdByte & 0xC0) == 0x80) &&
|
||||
((fourthByte & 0xC0) == 0x80)) // // Make sure the trailing bytes are correct
|
||||
{
|
||||
const uint32 secondOctet = static_cast<uint32>(secondByte & 0x1F) << 12;
|
||||
const uint32 thirdOctet = static_cast<uint32>(thirdByte & 0x3F) << 6;
|
||||
const uint32 fourthOctet = static_cast<uint32>(fourthByte & 0x3F);
|
||||
const uint32 result = ((octet & 0x07) << 18) | secondOctet | thirdOctet | fourthOctet;
|
||||
if (result >= 0x10000) // If smaller its an overlong
|
||||
{
|
||||
inStr.Data += 4;
|
||||
inStr.Size -= 1;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
isOverlong = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
isInvalid = true;
|
||||
}
|
||||
}
|
||||
LogError(LogCategory::Core, "StepUTF8: Non supported codepoint. IsOverlong: %s. IsInvalid %s. IsUTF16Surrogate %s",
|
||||
isOverlong ? "true" : "false", isInvalid ? "true" : "false", isUTF16Surrogate ? "true" : "false");
|
||||
inStr.Data += 1;
|
||||
return kInvalidUTF8;
|
||||
}
|
||||
|
||||
String FindString(String haystack, String needle)
|
||||
{
|
||||
if (!IsValid(needle))
|
||||
{
|
||||
return haystack;
|
||||
}
|
||||
|
||||
for (; IsValid(haystack); StepUTF8(haystack))
|
||||
{
|
||||
String tempHaystack = haystack;
|
||||
String testNeedle = needle;
|
||||
|
||||
while (IsValid(tempHaystack) && IsValid(testNeedle))
|
||||
{
|
||||
uint32 codepointHaystack = StepUTF8(tempHaystack);
|
||||
uint32 codepointNeedle = StepUTF8(testNeedle);
|
||||
if (codepointHaystack != codepointNeedle)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValid(testNeedle))
|
||||
{
|
||||
return haystack;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int8 StringCompareCaseInsensitive(String str1, String str2)
|
||||
{
|
||||
@@ -113,13 +222,13 @@ namespace Juliet
|
||||
{
|
||||
{
|
||||
uint32 leftFolded[3];
|
||||
int8 num_folded = CaseFoldUnicode(StepUTF8(str1, 4), leftFolded);
|
||||
int8 num_folded = CaseFoldUnicode(StepUTF8(str1), leftFolded);
|
||||
Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii)
|
||||
left = leftFolded[0];
|
||||
}
|
||||
{
|
||||
uint32 rightFolded[3];
|
||||
int8 num_folded = CaseFoldUnicode(StepUTF8(str2, 4), rightFolded);
|
||||
int8 num_folded = CaseFoldUnicode(StepUTF8(str2), rightFolded);
|
||||
Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii)
|
||||
right = rightFolded[0];
|
||||
}
|
||||
@@ -139,15 +248,16 @@ namespace Juliet
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ConvertString(StringEncoding from, StringEncoding to, const char* src, size_t srcLen, char*& dst, size_t& dstLen, size_t dstCapacity)
|
||||
bool ConvertString(StringEncoding from, StringEncoding to, String src, StringBuffer& dst)
|
||||
{
|
||||
Assert(src && *src);
|
||||
Assert(IsValid(src));
|
||||
|
||||
const char* srcStr = src;
|
||||
char* dstStr = dst;
|
||||
const char* srcStr = src.Data;
|
||||
char* dstStr = dst.Data;
|
||||
size_t remainingCapacity = dst.Capacity;
|
||||
|
||||
uint32 character = 0;
|
||||
while (srcLen > 0)
|
||||
while (src.Size > 0)
|
||||
{
|
||||
// Decode in character
|
||||
switch (from)
|
||||
@@ -165,7 +275,7 @@ namespace Juliet
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p[0] == 0xF0 && srcLen > 1 && (p[1] & 0xF0) == 0x80)
|
||||
if (p[0] == 0xF0 && src.Size > 1 && (p[1] & 0xF0) == 0x80)
|
||||
{
|
||||
overlong = true;
|
||||
}
|
||||
@@ -181,7 +291,7 @@ namespace Juliet
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p[0] == 0xE0 && srcLen > 1 && (p[1] & 0xE0) == 0x80)
|
||||
if (p[0] == 0xE0 && src.Size > 1 && (p[1] & 0xE0) == 0x80)
|
||||
{
|
||||
overlong = true;
|
||||
}
|
||||
@@ -217,8 +327,8 @@ namespace Juliet
|
||||
}
|
||||
}
|
||||
++srcStr;
|
||||
--srcLen;
|
||||
if (srcLen < left)
|
||||
--src.Size;
|
||||
if (src.Size < left)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Failed to convert string. Incomplete input sequence");
|
||||
return false;
|
||||
@@ -234,7 +344,7 @@ namespace Juliet
|
||||
character <<= 6;
|
||||
character |= (p[0] & 0x3F);
|
||||
++srcStr;
|
||||
--srcLen;
|
||||
--src.Size;
|
||||
}
|
||||
if (overlong)
|
||||
{
|
||||
@@ -268,7 +378,7 @@ namespace Juliet
|
||||
}
|
||||
if (character < 0x10000)
|
||||
{
|
||||
if (dstCapacity < 2)
|
||||
if (remainingCapacity < 2)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Destination buffer too short to fit UTF16");
|
||||
return false;
|
||||
@@ -277,12 +387,12 @@ namespace Juliet
|
||||
p[0] = static_cast<uint8>(character);
|
||||
|
||||
dstStr += 2;
|
||||
dstLen += 1;
|
||||
dstCapacity -= 2;
|
||||
dst.Size += 1;
|
||||
remainingCapacity -= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dstCapacity < 4)
|
||||
if (remainingCapacity < 4)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Destination buffer too short to fit UTF16");
|
||||
return false;
|
||||
@@ -296,8 +406,8 @@ namespace Juliet
|
||||
p[2] = static_cast<uint8>(word2);
|
||||
|
||||
dstStr += 4;
|
||||
dstLen += 1;
|
||||
dstCapacity -= 4;
|
||||
dst.Size += 1;
|
||||
remainingCapacity -= 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -313,10 +423,10 @@ namespace Juliet
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertString(String from, String to, const char* src, size_t srcSize, char*& dst, size_t& dstSize, size_t dstCapacity)
|
||||
bool ConvertString(String from, String to, String src, StringBuffer& dst)
|
||||
{
|
||||
Assert(StringIsValid(from));
|
||||
Assert(StringIsValid(to));
|
||||
Assert(IsValid(from));
|
||||
Assert(IsValid(to));
|
||||
|
||||
// First find the encoding of the strings
|
||||
auto sourceFormat = StringEncoding::Unknown;
|
||||
@@ -346,6 +456,6 @@ namespace Juliet
|
||||
return false;
|
||||
}
|
||||
|
||||
return ConvertString(sourceFormat, destFormat, src, srcSize, dst, dstSize, dstCapacity);
|
||||
return ConvertString(sourceFormat, destFormat, src, dst);
|
||||
}
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Juliet
|
||||
|
||||
String GetBasePath()
|
||||
{
|
||||
if (!StringIsValid(CachedBasePath))
|
||||
if (!IsValid(CachedBasePath))
|
||||
{
|
||||
CachedBasePath = Platform::GetBasePath();
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Juliet
|
||||
|
||||
void ShutdownFilesystem()
|
||||
{
|
||||
if (StringIsValid(CachedBasePath))
|
||||
if (IsValid(CachedBasePath))
|
||||
{
|
||||
CachedBasePath.Size = 0;
|
||||
SafeFree(CachedBasePath.Data);
|
||||
|
||||
@@ -11,12 +11,12 @@ namespace Juliet
|
||||
{
|
||||
IOStream* IOFromFile(String filename, String mode)
|
||||
{
|
||||
if (!StringIsValid(filename))
|
||||
if (!IsValid(filename))
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "Trying to open IOStream on invalid filename");
|
||||
return nullptr;
|
||||
}
|
||||
if (!StringIsValid(mode))
|
||||
if (!IsValid(mode))
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "Trying to open IOStream with invalid mode");
|
||||
return nullptr;
|
||||
@@ -54,10 +54,11 @@ namespace Juliet
|
||||
|
||||
Assert(writtenSize >= 0);
|
||||
|
||||
return IOWrite(stream, formattedBuffer, static_cast<size_t>(writtenSize));
|
||||
ByteBuffer buffer = {.Data = reinterpret_cast<Byte*>(formattedBuffer), .Size = static_cast<size_t>(writtenSize) };
|
||||
return IOWrite(stream, buffer);
|
||||
}
|
||||
|
||||
size_t IOWrite(NonNullPtr<IOStream> stream, const void* ptr, size_t size)
|
||||
size_t IOWrite(NonNullPtr<IOStream> stream, ByteBuffer inBuffer)
|
||||
{
|
||||
if (!stream->Interface.Write)
|
||||
{
|
||||
@@ -68,12 +69,12 @@ namespace Juliet
|
||||
|
||||
stream->Status = IOStreamStatus::Ready;
|
||||
|
||||
if (size == 0)
|
||||
if (!IsValid(inBuffer))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t writtenBytes = stream->Interface.Write(stream->Data, ptr, size, &stream->Status);
|
||||
size_t writtenBytes = stream->Interface.Write(stream->Data, inBuffer, &stream->Status);
|
||||
if ((writtenBytes == 0) && (stream->Status == IOStreamStatus::Ready))
|
||||
{
|
||||
stream->Status = IOStreamStatus::Error;
|
||||
@@ -131,23 +132,32 @@ namespace Juliet
|
||||
return stream->Interface.Size(stream->Data);
|
||||
}
|
||||
|
||||
uint8* LoadFile(String filename, size_t& outByteRead)
|
||||
ByteBuffer LoadFile(String filename)
|
||||
{
|
||||
IOStream* stream = IOFromFile(filename, ConstString("rb"));
|
||||
IOStream* stream = IOFromFile(filename, WrapString("rb"));
|
||||
if (!stream)
|
||||
{
|
||||
outByteRead = 0;
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
return LoadFile(stream, outByteRead, true);
|
||||
return LoadFile(stream, true);
|
||||
}
|
||||
|
||||
uint8* LoadFile(NonNullPtr<IOStream> stream, size_t& outByteRead, bool closeStreamWhenDone)
|
||||
ByteBuffer LoadFile(NonNullPtr<IOStream> stream, bool closeStreamWhenDone)
|
||||
{
|
||||
constexpr size_t kFileChunkSize = 1024;
|
||||
uint8* data = nullptr;
|
||||
uint8* newData = nullptr;
|
||||
size_t totalSize = 0;
|
||||
ByteBuffer resultBuffer = {};
|
||||
|
||||
auto deferred = Defer(
|
||||
[&]()
|
||||
{
|
||||
if (closeStreamWhenDone)
|
||||
{
|
||||
IOClose(stream);
|
||||
}
|
||||
});
|
||||
|
||||
// Try reading the size from the stream, if failing we'll try to read it chunk by chunk
|
||||
bool loadChunks = false;
|
||||
@@ -160,7 +170,7 @@ namespace Juliet
|
||||
data = static_cast<uint8*>(Malloc(static_cast<size_t>(size + 1)));
|
||||
if (!data)
|
||||
{
|
||||
goto done;
|
||||
return {};
|
||||
}
|
||||
|
||||
while (true)
|
||||
@@ -174,8 +184,7 @@ namespace Juliet
|
||||
if (!newData)
|
||||
{
|
||||
Free(data);
|
||||
data = nullptr;
|
||||
goto done;
|
||||
return {};
|
||||
}
|
||||
data = newData;
|
||||
}
|
||||
@@ -201,14 +210,10 @@ namespace Juliet
|
||||
// Adding null terminator
|
||||
data[totalSize] = '\0';
|
||||
|
||||
done:
|
||||
outByteRead = totalSize;
|
||||
resultBuffer.Data = reinterpret_cast<Byte*>(data);
|
||||
resultBuffer.Size = totalSize;
|
||||
|
||||
if (closeStreamWhenDone)
|
||||
{
|
||||
IOClose(stream);
|
||||
}
|
||||
return data;
|
||||
return resultBuffer;
|
||||
}
|
||||
|
||||
bool IOClose(NonNullPtr<IOStream> stream)
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace Juliet::Internal
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
size_t FileWrite(NonNullPtr<IOStreamDataPayload> payload, const void* inBuffer, size_t size, NonNullPtr<IOStreamStatus> status)
|
||||
size_t FileWrite(NonNullPtr<IOStreamDataPayload> payload, ByteBuffer inBuffer, NonNullPtr<IOStreamStatus> status)
|
||||
{
|
||||
auto win32Payload = static_cast<Win32IOStreamDataPayload*>(payload.Get());
|
||||
DWORD bytes;
|
||||
@@ -158,12 +158,12 @@ namespace Juliet::Internal
|
||||
}
|
||||
}
|
||||
|
||||
if (!WriteFile(win32Payload->Handle, inBuffer, static_cast<DWORD>(size), &bytes, nullptr))
|
||||
if (!WriteFile(win32Payload->Handle, inBuffer.Data, static_cast<DWORD>(inBuffer.Size), &bytes, nullptr))
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "IoStream:FileWrite:Error writing to datastream: %d", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
if (bytes == 0 && size > 0)
|
||||
if (bytes == 0 && inBuffer.Size > 0)
|
||||
{
|
||||
*status = IOStreamStatus::NotReady;
|
||||
}
|
||||
@@ -199,18 +199,17 @@ namespace Juliet::Internal
|
||||
|
||||
#if _DEBUG
|
||||
// Making sure the mode is valid
|
||||
String modeView = mode;
|
||||
size_t modeLength = StringLength(modeView);
|
||||
size_t modeLength = StringLength(mode);
|
||||
Assert((modeLength <= 2) &&
|
||||
"Mode should have at most 2 characters, one being either r,w or a and the other can only be +");
|
||||
if (modeLength == 1)
|
||||
{
|
||||
Assert((modeView.Data[0] == 'r' || modeView.Data[0] == 'w' || modeView.Data[0] == 'a') &&
|
||||
Assert((mode.Data[0] == 'r' || mode.Data[0] == 'w' || mode.Data[0] == 'a') &&
|
||||
"Invalid Mode. First char is not r,w or a");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert((modeView.Data[1] == '+' || modeView.Data[1] == 'b') && "Invalid Mode. Second char is not +");
|
||||
Assert((mode.Data[1] == '+' || mode.Data[1] == 'b') && "Invalid Mode. Second char is not +");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -229,11 +228,12 @@ namespace Juliet::Internal
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: Detect when folder is missing and create it if in w or a mode.
|
||||
HANDLE hFile = CreateFileA(CStr(filename), (canWrite | canRead), (canWrite) ? 0 : FILE_SHARE_READ, nullptr,
|
||||
(openExisting | createAlways | openAlways), FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Core, "IOFromFile: couldn't open %s", CStr(filename));
|
||||
Log(LogLevel::Error, LogCategory::Core, "IOFromFile: couldn't open %s. Error: %d", CStr(filename), GetLastError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,15 +87,12 @@ namespace Juliet
|
||||
LogManagerSingleton.Shutdown();
|
||||
}
|
||||
|
||||
void Log(LogLevel level, LogCategory category, const char* fmt, ...)
|
||||
void Log(LogLevel level, LogCategory category, const char* fmt, va_list args)
|
||||
{
|
||||
// TODO : Revisit, copy from https://github.com/Eclmist/Ether/blob/develop/src/common/logging/loggingmanager.cpp
|
||||
char formattedBuffer[4096];
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
(void)vsprintf_s(formattedBuffer, fmt, args); // Cast to void to ignore the return type. TODO : Juliet format function
|
||||
va_end(args);
|
||||
|
||||
std::string formattedText(formattedBuffer);
|
||||
|
||||
@@ -114,4 +111,35 @@ namespace Juliet
|
||||
}
|
||||
}
|
||||
|
||||
void Log(LogLevel level, LogCategory category, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log(level, category, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogMessage(LogCategory category, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log(LogLevel::Message, category, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogWarning(LogCategory category, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log(LogLevel::Warning, category, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogError(LogCategory category, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
Log(LogLevel::Error, category, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
} // namespace Juliet
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <Core/Networking/NetworkPacket.h>
|
||||
#include <Core/Networking/SocketPlatformImpl.h>
|
||||
|
||||
#define TO_BUFFER(ptr) reinterpret_cast<const Byte*>(ptr)
|
||||
#define TO_BUFFER(ptr) reinterpret_cast<Byte*>(ptr)
|
||||
|
||||
namespace Juliet
|
||||
{
|
||||
@@ -29,12 +29,12 @@ namespace Juliet
|
||||
// Begin - Pack
|
||||
NetworkPacket& NetworkPacket::operator<<(uint32 value)
|
||||
{
|
||||
const uint32 toWrite = htonl(value);
|
||||
uint32 toWrite = htonl(value);
|
||||
Append({ TO_BUFFER(&toWrite), sizeof(toWrite) });
|
||||
return *this;
|
||||
}
|
||||
|
||||
NetworkPacket& NetworkPacket::operator<<(const char* data)
|
||||
NetworkPacket& NetworkPacket::operator<<(char* data)
|
||||
{
|
||||
Assert(data && "NetworkPacket::operator<< Data must not be null");
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
|
||||
#include <pch.h>
|
||||
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <Core/Memory/Utils.h>
|
||||
#include <Graphics/D3D12/D3D12Common.h>
|
||||
#include <Graphics/D3D12/DX12GraphicsDevice.h>
|
||||
#include <Graphics/D3D12/DX12Includes.h>
|
||||
|
||||
@@ -44,9 +44,9 @@ namespace
|
||||
|
||||
void JulietApplication::Init()
|
||||
{
|
||||
Log(LogLevel::Message, LogCategory::Editor, "Initializing Juliet Application...");
|
||||
Log(LogLevel::Message, LogCategory::Tool, "Initializing Juliet Application...");
|
||||
|
||||
Log(LogLevel::Message, LogCategory::Editor, "%s", CStr(GetBasePath()));
|
||||
Log(LogLevel::Message, LogCategory::Tool, "%s", CStr(GetBasePath()));
|
||||
|
||||
GraphicsConfig config;
|
||||
GraphicsDevice = CreateGraphicsDevice(config);
|
||||
@@ -72,7 +72,7 @@ void JulietApplication::Init()
|
||||
|
||||
void JulietApplication::Shutdown()
|
||||
{
|
||||
Log(LogLevel::Message, LogCategory::Editor, "Shutting down Juliet Application...");
|
||||
Log(LogLevel::Message, LogCategory::Tool, "Shutting down Juliet Application...");
|
||||
|
||||
Game.Shutdown();
|
||||
ShutdownHotReloadCode(GameCode);
|
||||
@@ -92,7 +92,7 @@ void JulietApplication::Shutdown()
|
||||
DestroyGraphicsDevice(GraphicsDevice);
|
||||
}
|
||||
|
||||
Log(LogLevel::Message, LogCategory::Editor, "Juliet App shutdown Completed");
|
||||
Log(LogLevel::Message, LogCategory::Tool, "Juliet App shutdown Completed");
|
||||
}
|
||||
|
||||
void JulietApplication::Update()
|
||||
@@ -121,7 +121,7 @@ void JulietApplication::Update()
|
||||
CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics);
|
||||
if (cmdList == nullptr)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Editor, "Failed to acquire command list.");
|
||||
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire command list.");
|
||||
Running = false;
|
||||
return;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ void JulietApplication::Update()
|
||||
Texture* swapChainTexture = nullptr;
|
||||
if (!AcquireSwapChainTexture(cmdList, MainWindow, &swapChainTexture))
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Editor, "Failed to acquire swapchain texture.");
|
||||
Log(LogLevel::Error, LogCategory::Tool, "Failed to acquire swapchain texture.");
|
||||
Running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,9 +99,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DXCompiler.h"/>
|
||||
<ClCompile Include="ShaderCompiler.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="DXShaderCompiler\dxcompiler.dll">
|
||||
@@ -111,6 +109,10 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</CopyFileToFolders>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="DXShaderCompiler\DXCompiler.h" />
|
||||
<ClInclude Include="ShaderCompiler.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets"/>
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
199
JulietShaderCompiler/ShaderCompiler.cpp
Normal file
199
JulietShaderCompiler/ShaderCompiler.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
#include <ShaderCompiler.h>
|
||||
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Logging/LogTypes.h>
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <DXShaderCompiler/DXCompiler.h>
|
||||
|
||||
#pragma comment(lib, "dxcompiler.lib")
|
||||
|
||||
using namespace Juliet;
|
||||
|
||||
namespace
|
||||
{
|
||||
ByteBuffer CompileWithDXC(HLSLShaderInfo& info, bool compileToSpirv)
|
||||
{
|
||||
Assert(!compileToSpirv && "SPIRV is not supported yet");
|
||||
|
||||
IDxcCompiler3* compiler = nullptr;
|
||||
DxcCreateInstance(&CLSID_DxcCompiler, IID_IDxcCompiler3, reinterpret_cast<void**>(&compiler));
|
||||
|
||||
IDxcUtils* utils = nullptr;
|
||||
DxcCreateInstance(&CLSID_DxcUtils, &IID_IDxcUtils, reinterpret_cast<void**>(&utils));
|
||||
|
||||
if (compiler == nullptr)
|
||||
{
|
||||
LogError(LogCategory::Tool, "Cannot create DXCompiler instance");
|
||||
return {};
|
||||
}
|
||||
|
||||
IDxcResult* dxcResult = nullptr;
|
||||
LPCWSTR* args = nullptr;
|
||||
auto deferred = Defer(
|
||||
[&]()
|
||||
{
|
||||
SafeFree(args);
|
||||
if (dxcResult)
|
||||
{
|
||||
dxcResult->lpVtbl->Release(dxcResult);
|
||||
}
|
||||
if (compiler)
|
||||
{
|
||||
compiler->lpVtbl->Release(compiler);
|
||||
}
|
||||
if (utils)
|
||||
{
|
||||
utils->lpVtbl->Release(utils);
|
||||
}
|
||||
});
|
||||
|
||||
if (utils == nullptr)
|
||||
{
|
||||
LogError(LogCategory::Graphics, "Cannot create IDxcUtils instance");
|
||||
return {};
|
||||
}
|
||||
|
||||
IDxcIncludeHandler* includeHandler;
|
||||
utils->lpVtbl->CreateDefaultIncludeHandler(utils, &includeHandler);
|
||||
if (includeHandler == nullptr)
|
||||
{
|
||||
LogError(LogCategory::Graphics, "Cannot create Default include handler");
|
||||
return {};
|
||||
}
|
||||
|
||||
StringBuffer convertedBuffer = {};
|
||||
char inplaceBuffer[1024];
|
||||
convertedBuffer.Data = inplaceBuffer;
|
||||
convertedBuffer.Capacity = sizeof(inplaceBuffer);
|
||||
if (!ConvertString(StringEncoding::UTF8, StringEncoding::UTF16, info.EntryPoint, convertedBuffer))
|
||||
{
|
||||
LogError(LogCategory::Tool, "Cannot convert entry point string to utf16");
|
||||
return {};
|
||||
}
|
||||
|
||||
// Creating args for the compiler
|
||||
// Allocating enough to fit them all
|
||||
static constexpr size_t kExpectedArgCount = 32;
|
||||
uint32 argCount = 0;
|
||||
args = static_cast<LPCWSTR*>(Calloc(kExpectedArgCount, sizeof(LPCWSTR)));
|
||||
if (!args)
|
||||
{
|
||||
LogError(LogCategory::Tool, "Couldn't allocate Args array");
|
||||
return {};
|
||||
}
|
||||
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-E");
|
||||
args[argCount++] = reinterpret_cast<LPCWSTR>(inplaceBuffer);
|
||||
|
||||
DxcBuffer source;
|
||||
source.Ptr = info.ByteCodeBuffer.Data;
|
||||
source.Size = info.ByteCodeBuffer.Size;
|
||||
source.Encoding = DXC_CP_ACP;
|
||||
|
||||
switch (info.Stage)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
{
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-T");
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"vs_6_0");
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Fragment:
|
||||
{
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-T");
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"ps_6_0");
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Compute:
|
||||
{
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-T");
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"cs_6_0");
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Invalid:
|
||||
{
|
||||
Assert(false && "Invalid shader stage");
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (compileToSpirv)
|
||||
{
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-spirv");
|
||||
args[argCount++] = const_cast<LPCWSTR>(L"-fspv-flatten-resource-arrays");
|
||||
}
|
||||
|
||||
char nameInplaceBuffer[1024];
|
||||
if (IsValid(info.Name))
|
||||
{
|
||||
StringBuffer nameUtf16 = {};
|
||||
nameUtf16.Data = nameInplaceBuffer;
|
||||
nameUtf16.Capacity = sizeof(nameInplaceBuffer);
|
||||
if (ConvertString(StringEncoding::UTF8, StringEncoding::UTF16, info.Name, nameUtf16))
|
||||
{
|
||||
args[argCount++] = reinterpret_cast<LPCWSTR>(nameInplaceBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT result = compiler->lpVtbl->Compile(compiler, &source, args, argCount, includeHandler, IID_IDxcResult,
|
||||
reinterpret_cast<void**>(&dxcResult));
|
||||
if (result < 0)
|
||||
{
|
||||
LogError(LogCategory::Tool, "IDxcShaderCompiler3::Compile failed: %X", result);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (dxcResult == nullptr)
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s", "HLSL compilation failed with no IDxcResult");
|
||||
return {};
|
||||
}
|
||||
|
||||
IDxcBlob* blob = nullptr;
|
||||
IDxcBlobUtf8* errors = nullptr;
|
||||
result = dxcResult->lpVtbl->GetOutput(dxcResult, DXC_OUT_OBJECT, IID_IDxcBlob, reinterpret_cast<void**>(&blob), nullptr);
|
||||
if (result < 0)
|
||||
{
|
||||
// Compilation failed, display errors
|
||||
dxcResult->lpVtbl->GetOutput(dxcResult, DXC_OUT_ERRORS, IID_IDxcBlobUtf8, reinterpret_cast<void**>(&errors), nullptr);
|
||||
if (errors != nullptr && errors->lpVtbl->GetBufferSize(errors) != 0)
|
||||
{
|
||||
LogError(LogCategory::Tool, "HLSL compilation failed: %s",
|
||||
static_cast<char*>(errors->lpVtbl->GetBufferPointer(errors)));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError(LogCategory::Tool, "Compilation failed with unknown error");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// If compilation succeeded, but there are errors, those are warnings
|
||||
dxcResult->lpVtbl->GetOutput(dxcResult, DXC_OUT_ERRORS, IID_IDxcBlobUtf8, reinterpret_cast<void**>(&errors), nullptr);
|
||||
if (errors != nullptr && errors->lpVtbl->GetBufferSize(errors) != 0)
|
||||
{
|
||||
LogWarning(LogCategory::Tool, "HLSL compiled with warnings: %s",
|
||||
static_cast<char*>(errors->lpVtbl->GetBufferPointer(errors)));
|
||||
}
|
||||
|
||||
// TODO: Scratch allocator. No malloc
|
||||
ByteBuffer buffer = {};
|
||||
buffer.Size = blob->lpVtbl->GetBufferSize(blob);
|
||||
buffer.Data = static_cast<Byte*>(Malloc(buffer.Size));
|
||||
MemCopy(buffer.Data, blob->lpVtbl->GetBufferPointer(blob), buffer.Size);
|
||||
|
||||
blob->lpVtbl->Release(blob);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ByteBuffer CompileDXIL(HLSLShaderInfo& info)
|
||||
{
|
||||
// TODO: Use SPIRV an do hlsl -> spirv -> hlsl -> dxil.
|
||||
// This is from Shadercross SDL_ShaderCross_CompileDXILFromHLSL.
|
||||
// Not sure why yet.
|
||||
|
||||
return CompileWithDXC(info, false);
|
||||
}
|
||||
42
JulietShaderCompiler/ShaderCompiler.h
Normal file
42
JulietShaderCompiler/ShaderCompiler.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Common/CoreTypes.h>
|
||||
#include <Core/Common/String.h>
|
||||
|
||||
enum class SourceLanguage : uint8
|
||||
{
|
||||
HLSL = 0,
|
||||
Count = 1,
|
||||
Invalid = Count
|
||||
};
|
||||
|
||||
enum class ShaderFormat : uint8
|
||||
{
|
||||
DXIL = 0,
|
||||
Count = 1,
|
||||
Invalid = Count
|
||||
};
|
||||
|
||||
enum class ShaderStage : uint8
|
||||
{
|
||||
Vertex = 0,
|
||||
Fragment = 1,
|
||||
Compute = 2,
|
||||
Count = 3,
|
||||
Invalid = Count
|
||||
};
|
||||
|
||||
struct ShaderInfo
|
||||
{
|
||||
ByteBuffer ByteCodeBuffer;
|
||||
Juliet::String EntryPoint;
|
||||
Juliet::String Name;
|
||||
ShaderStage Stage;
|
||||
};
|
||||
|
||||
struct HLSLShaderInfo : ShaderInfo
|
||||
{
|
||||
// TODO Defines and includes
|
||||
};
|
||||
|
||||
ByteBuffer CompileDXIL(HLSLShaderInfo& info);
|
||||
@@ -1,56 +1,229 @@
|
||||
#include <Core/Common/EnumUtils.h>
|
||||
#include <Core/Common/String.h>
|
||||
#include <Core/HAL/IO/IOStream.h>
|
||||
#include <Core/Logging/LogManager.h>
|
||||
#include <Core/Logging/LogTypes.h>
|
||||
|
||||
// Must be before everything else.
|
||||
// Cannot include DX12Includes because we redefine everything.
|
||||
// TODO : Separate lib
|
||||
// TODO : Only when not shipping
|
||||
// Check if we just load the dll and not link against?
|
||||
#include <DXCompiler.h>
|
||||
|
||||
#pragma comment(lib, "dxcompiler.lib")
|
||||
#include <Core/Memory/Allocator.h>
|
||||
#include <ShaderCompiler.h>
|
||||
|
||||
using namespace Juliet;
|
||||
|
||||
void Compile()
|
||||
void PrintHelp()
|
||||
{
|
||||
IDxcCompiler3* compiler = nullptr;
|
||||
DxcCreateInstance(&CLSID_DxcCompiler, IID_IDxcCompiler3, reinterpret_cast<void**>(&compiler));
|
||||
|
||||
IDxcUtils* utils = nullptr;
|
||||
DxcCreateInstance(&CLSID_DxcUtils, &IID_IDxcUtils, reinterpret_cast<void**>(&utils));
|
||||
|
||||
if (compiler == nullptr)
|
||||
{
|
||||
Juliet::Log(LogLevel::Error, LogCategory::Graphics, "Cannot create DXCompiler instance");
|
||||
}
|
||||
|
||||
if (utils == nullptr)
|
||||
{
|
||||
Log(LogLevel::Error, LogCategory::Graphics, "Cannot create IDxcUtils instance");
|
||||
compiler->lpVtbl->Release(compiler);
|
||||
}
|
||||
|
||||
IDxcIncludeHandler* includeHandler;
|
||||
utils->lpVtbl->CreateDefaultIncludeHandler(utils, &includeHandler);
|
||||
if (includeHandler == nullptr)
|
||||
{
|
||||
compiler->lpVtbl->Release(compiler);
|
||||
utils->lpVtbl->Release(utils);
|
||||
}
|
||||
LogMessage(LogCategory::Tool, "Usage: JulietShaderCompiler.exe <input> [options]");
|
||||
LogMessage(LogCategory::Tool, "Required Parameters:");
|
||||
static constexpr int kColumnWdith = 32;
|
||||
LogMessage(LogCategory::Tool, " %-*s %s", kColumnWdith, "-o | --output <value>", "Output file.");
|
||||
// LogMessage(LogCategory::Tool, "\n");
|
||||
// LogMessage(LogCategory::Tool, "Required Parameters that can be inferred from filename:");
|
||||
// LogMessage(LogCategory::Tool, " %-*s %s", kColumnWdith, "-s | --src <value>", "Source Language. Values: [HLSL*] | * Default");
|
||||
// LogMessage(LogCategory::Tool, " %-*s %s", kColumnWdith, "-d | --dest <value>", "Destination Format. Values: [DXIL*] | * Default");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// auto* stream = IOFromFile(ConstString("XF"), ConstString("w"));
|
||||
String filename = {};
|
||||
ByteBuffer fileData = {};
|
||||
|
||||
String outputFilename = {};
|
||||
|
||||
String entryPoint = WrapString("main");
|
||||
|
||||
bool sourceLangDetected = false;
|
||||
auto srcLanguage = SourceLanguage::Invalid;
|
||||
|
||||
bool dstShaderFormatDetected = false;
|
||||
auto dstShaderFormat = ShaderFormat::Invalid;
|
||||
|
||||
bool shaderStageDetected = false;
|
||||
auto shaderStage = ShaderStage::Invalid;
|
||||
|
||||
IOStream* outStream = nullptr;
|
||||
|
||||
auto deferred = Defer(
|
||||
[&]()
|
||||
{
|
||||
if (IsValid(fileData))
|
||||
{
|
||||
Free(fileData);
|
||||
}
|
||||
if (outStream)
|
||||
{
|
||||
IOClose(outStream);
|
||||
}
|
||||
// Pause here to not close the console window immediately on stop
|
||||
system("PAUSE");
|
||||
});
|
||||
|
||||
for (int idx = 1; idx < argc; ++idx)
|
||||
{
|
||||
String arg = WrapString(argv[idx]);
|
||||
if (CStr(arg)[0] == '-')
|
||||
{
|
||||
if (StringCompareCaseInsensitive(arg, ConstString("-h")) == 0 ||
|
||||
StringCompareCaseInsensitive(arg, ConstString("--help")) == 0)
|
||||
{
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (StringCompareCaseInsensitive(arg, ConstString("-o")) == 0 ||
|
||||
StringCompareCaseInsensitive(arg, ConstString("--output")) == 0)
|
||||
{
|
||||
if (idx + 1 >= argc)
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s requires an argument", argv);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
idx += 1;
|
||||
outputFilename = WrapString(argv[idx]);
|
||||
}
|
||||
}
|
||||
else if (IsValid(filename) == false)
|
||||
{
|
||||
filename = arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s: Unknown parameter: %s", argv[0], CStr(arg));
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsValid(filename))
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s: missing input filename path", argv[0]);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
if (!IsValid(outputFilename))
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s: missing output path", argv[0]);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
fileData = LoadFile(filename);
|
||||
if (!IsValid(fileData))
|
||||
{
|
||||
LogError(LogCategory::Tool, "Invalid file: %s", CStr(filename));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Find the source language from filename
|
||||
{
|
||||
if (!sourceLangDetected)
|
||||
{
|
||||
if (IsValid(FindString(filename, WrapString("hlsl"))))
|
||||
{
|
||||
srcLanguage = SourceLanguage::HLSL;
|
||||
}
|
||||
static_assert(ToUnderlying(SourceLanguage::Count) == 1, "SourceLanguage must be one of: HLSL");
|
||||
}
|
||||
|
||||
if (srcLanguage == SourceLanguage::Invalid)
|
||||
{
|
||||
LogError(LogCategory::Tool,
|
||||
"%s: Invalid source language. Please either have a valid file extension or use -s/--src", argv[0]);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the destination format from output file name
|
||||
{
|
||||
if (!dstShaderFormatDetected)
|
||||
{
|
||||
if (IsValid(FindString(outputFilename, WrapString("dxil"))))
|
||||
{
|
||||
dstShaderFormat = ShaderFormat::DXIL;
|
||||
}
|
||||
static_assert(ToUnderlying(ShaderFormat::Count) == 1, "ShaderFormat must be one of: DXIL");
|
||||
|
||||
if (dstShaderFormat == ShaderFormat::Invalid)
|
||||
{
|
||||
LogError(LogCategory::Tool,
|
||||
"%s: Invalid shader format. Please either have a valid file extension or use -d/--dest", argv[0]);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find shader stage from filename
|
||||
{
|
||||
if (!shaderStageDetected)
|
||||
{
|
||||
// TODO Need a case insensitive FindString
|
||||
if (IsValid(FindString(filename, WrapString("vert"))))
|
||||
{
|
||||
shaderStage = ShaderStage::Vertex;
|
||||
}
|
||||
else if (IsValid(FindString(filename, WrapString("frag"))))
|
||||
{
|
||||
shaderStage = ShaderStage::Fragment;
|
||||
}
|
||||
else if (IsValid(FindString(filename, WrapString("comp"))))
|
||||
{
|
||||
shaderStage = ShaderStage::Compute;
|
||||
}
|
||||
static_assert(ToUnderlying(ShaderFormat::Count) == 1, "ShaderStage must be one of: vert, frag, comp");
|
||||
|
||||
if (shaderStage == ShaderStage::Invalid)
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s: Invalid shader stage. Please include it in the filename", argv[0]);
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outStream = IOFromFile(outputFilename, WrapString("w"));
|
||||
if (!outStream)
|
||||
{
|
||||
LogError(LogCategory::Tool, "%s: Failed to open %s for writing", argv[0], CStr(outputFilename));
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (srcLanguage)
|
||||
{
|
||||
case SourceLanguage::HLSL:
|
||||
{
|
||||
HLSLShaderInfo hlslInfo = {};
|
||||
hlslInfo.ByteCodeBuffer = fileData;
|
||||
hlslInfo.EntryPoint = entryPoint;
|
||||
hlslInfo.Name = filename;
|
||||
hlslInfo.Stage = shaderStage;
|
||||
|
||||
switch (dstShaderFormat)
|
||||
{
|
||||
case ShaderFormat::DXIL:
|
||||
{
|
||||
ByteBuffer compiledBuffer = CompileDXIL(hlslInfo);
|
||||
if (!IsValid(compiledBuffer))
|
||||
{
|
||||
LogError(LogCategory::Tool, "Failed to compile DXBC from HLSL. Error: %s", "Unknown");
|
||||
}
|
||||
else
|
||||
{
|
||||
IOWrite(outStream, compiledBuffer);
|
||||
Free(compiledBuffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ShaderFormat::Invalid: Assert(false && "Shader format is invalid"); return 1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SourceLanguage::Invalid: Assert(false && "Source Language is invalid"); return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// IOPrintf(stream, ":)");
|
||||
// IOPrintf(stream, "%d%s", 1234, "Hello World!");
|
||||
|
||||
size_t bytesRead = 0;
|
||||
auto* data = LoadFile(ConstString("XF"),bytesRead );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user