Finished first version of shader compiler. HLSL -> DXIL.

Submitting vertex and frag shader needed to display a triangle.
This commit is contained in:
2025-03-08 22:36:15 -05:00
parent f9f292b6d6
commit da203c80f3
27 changed files with 837 additions and 173 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
float4 main(float4 Color : TEXCOORD0) : SV_Target0
{
return Color;
}

View 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;
}

View File

@@ -20,7 +20,7 @@ using size_t = std::size_t;
struct ByteBuffer
{
const Byte* Data;
Byte* Data;
size_t Size;
};

View File

@@ -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)
{

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -15,7 +15,7 @@ namespace Juliet
Graphics = 1,
Networking = 2,
Engine = 3,
Editor = 4,
Tool = 4,
Game = 5,
};
} // namespace Juliet

View File

@@ -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;
}
}

View File

@@ -19,4 +19,5 @@ namespace Juliet
}
#define MemSet memset
#define MemCopy memcpy
} // namespace Juliet

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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");

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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>

View 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);
}

View 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);

View File

@@ -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;
}