From 8c6c42e12335f93508ab86807dadae2900819323 Mon Sep 17 00:00:00 2001 From: Patedam Date: Sun, 9 Mar 2025 15:54:13 -0400 Subject: [PATCH] Shader: Added code to load compiled shader --- Juliet/Juliet.vcxproj | 1 + .../include/Core/HAL/Filesystem/Filesystem.h | 2 + Juliet/include/Core/Memory/Utils.h | 2 + Juliet/include/Graphics/Graphics.h | 6 +++ Juliet/include/Graphics/Shader.h | 23 ++++++++++ Juliet/src/Core/HAL/Filesystem/Filesystem.cpp | 9 ++++ .../Core/HAL/Filesystem/Filesystem_Platform.h | 3 +- .../HAL/Filesystem/Win32/Win32Filesystem.cpp | 41 +++++++++++++++-- .../src/Core/HAL/IO/Win32/Win32IOStream.cpp | 20 ++++----- Juliet/src/Graphics/D3D12/D3D12Shader.cpp | 44 +++++++++++++++++-- Juliet/src/Graphics/D3D12/D3D12Shader.h | 9 +++- .../src/Graphics/D3D12/DX12GraphicsDevice.cpp | 3 ++ Juliet/src/Graphics/Graphics.cpp | 40 ++++++++++++++++- Juliet/src/Graphics/GraphicsDevice.h | 4 ++ JulietApp/main.cpp | 29 +++++++++--- 15 files changed, 209 insertions(+), 27 deletions(-) create mode 100644 Juliet/include/Graphics/Shader.h diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj index 70407d8..c23400c 100644 --- a/Juliet/Juliet.vcxproj +++ b/Juliet/Juliet.vcxproj @@ -145,6 +145,7 @@ + diff --git a/Juliet/include/Core/HAL/Filesystem/Filesystem.h b/Juliet/include/Core/HAL/Filesystem/Filesystem.h index 4e52792..4464117 100644 --- a/Juliet/include/Core/HAL/Filesystem/Filesystem.h +++ b/Juliet/include/Core/HAL/Filesystem/Filesystem.h @@ -6,4 +6,6 @@ namespace Juliet { // Returns the path to the application directory extern JULIET_API String GetBasePath(); + + extern JULIET_API bool IsAbsolutePath(String path); } // namespace Juliet diff --git a/Juliet/include/Core/Memory/Utils.h b/Juliet/include/Core/Memory/Utils.h index 2a9ad8b..de0c966 100644 --- a/Juliet/include/Core/Memory/Utils.h +++ b/Juliet/include/Core/Memory/Utils.h @@ -14,10 +14,12 @@ namespace Juliet { ++left; ++right; + --size; } return size ? *left - *right : 0; } + // TODO: homemade versions #define MemSet memset #define MemCopy memcpy } // namespace Juliet diff --git a/Juliet/include/Graphics/Graphics.h b/Juliet/include/Graphics/Graphics.h index 2f48680..f06f776 100644 --- a/Juliet/include/Graphics/Graphics.h +++ b/Juliet/include/Graphics/Graphics.h @@ -1,10 +1,12 @@ #pragma once #include +#include #include #include #include #include +#include #include // Graphics Interface @@ -104,4 +106,8 @@ namespace Juliet extern JULIET_API void SetScissorRect(NonNullPtr renderPass, const Rectangle& rectangle); extern JULIET_API void SetBlendConstants(NonNullPtr renderPass, FColor blendConstants); extern JULIET_API void SetStencilReference(NonNullPtr renderPass, uint8 reference); + + // Shaders + extern JULIET_API Shader* CreateShader(NonNullPtr device, String filename, ShaderCreateInfo& shaderCreateInfo); + extern JULIET_API void DestroyShader(NonNullPtr driver, NonNullPtr shader); } // namespace Juliet diff --git a/Juliet/include/Graphics/Shader.h b/Juliet/include/Graphics/Shader.h new file mode 100644 index 0000000..2fab3cc --- /dev/null +++ b/Juliet/include/Graphics/Shader.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace Juliet +{ + // Opaque type + struct Shader; + + enum class ShaderStage : uint8 + { + Vertex, + Fragment, + Compute + }; + + struct ShaderCreateInfo + { + ShaderStage Stage; + String EntryPoint; + }; + +} // namespace Juliet diff --git a/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp b/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp index 4c45768..dac7678 100644 --- a/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp +++ b/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp @@ -22,6 +22,15 @@ namespace Juliet return CachedBasePath; } + bool IsAbsolutePath(String path) + { + if (!IsValid(path)) + { + return false; + } + return Platform::IsAbsolutePath(path); + } + void InitFilesystem() {} void ShutdownFilesystem() diff --git a/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h b/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h index c67597a..fdaa89a 100644 --- a/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h +++ b/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h @@ -3,4 +3,5 @@ namespace Juliet::Platform { extern String GetBasePath(); -} + extern bool IsAbsolutePath(String path); +} // namespace Juliet::Platform diff --git a/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp b/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp index ed8fb0e..6671e6e 100644 --- a/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp +++ b/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp @@ -11,8 +11,8 @@ namespace Juliet::Platform { // Allocate a buffer that could fit the module size. // Max Path is a good start but could be bigger if the path include long path prefix - size_t bufferSize=MAX_PATH; - auto buffer = static_cast(Calloc(MAX_PATH, sizeof(char))); + size_t bufferSize = MAX_PATH; + auto buffer = static_cast(Calloc(MAX_PATH, sizeof(char))); if (buffer == nullptr) { return {}; @@ -41,7 +41,6 @@ namespace Juliet::Platform Log(LogLevel::Error, LogCategory::Core, "Filesystem: Cannot locate executable path"); } - size_t idx = 0; for (idx = moduleFilenameLength - 1; idx > 0; --idx) { @@ -56,4 +55,40 @@ namespace Juliet::Platform return WrapString(buffer); } + + bool IsAbsolutePath(String path) + { + if (path.Data || path.Size == 0) + { + return false; + } + + // From https://learn.microsoft.com/en-ca/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#fully_qualified_vs._relative_paths + // Windows path starts with either: + // A UNC name of any format, which always start with two backslash characters ("\\"). For more information, see the next section. + // A disk designator with a backslash, for example "C:\" or "d:\". + // A single backslash, for example, "\directory" or "\file.txt". This is also referred to as an absolute path. + // We will only handle the first two. Single backslash is weird. + const char* pathStr = path.Data; + size_t pathLen = path.Size; + + if (pathLen > 1) + { + if (pathStr[0] == '\\' && pathStr[1] == '\\') + { + return true; + } + + if (pathLen > 2) + { + char first = pathStr[0]; + char second = pathStr[1]; + char third = pathStr[2]; + return ((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z')) && second == ':' && + (third == '\\' || third == '/'); + } + } + + return false; + } } // namespace Juliet::Platform diff --git a/Juliet/src/Core/HAL/IO/Win32/Win32IOStream.cpp b/Juliet/src/Core/HAL/IO/Win32/Win32IOStream.cpp index f9b6c47..509bbd2 100644 --- a/Juliet/src/Core/HAL/IO/Win32/Win32IOStream.cpp +++ b/Juliet/src/Core/HAL/IO/Win32/Win32IOStream.cpp @@ -248,16 +248,16 @@ namespace Juliet::Internal return nullptr; } - IOStreamInterface interface = {}; - interface.Version = sizeof(interface); + IOStreamInterface iface = {}; + iface.Version = sizeof(iface); if (GetFileType(hFile) == FILE_TYPE_DISK) { - interface.Size = FileSize; - interface.Seek = FileSeek; + iface.Size = FileSize; + iface.Seek = FileSeek; } - interface.Read = FileRead; - interface.Write = FileWrite; - interface.Close = FileClose; + iface.Read = FileRead; + iface.Write = FileWrite; + iface.Close = FileClose; payload->Handle = hFile; payload->IsAppending = isAppending; @@ -266,14 +266,14 @@ namespace Juliet::Internal payload->Data = static_cast(Malloc(kFileReadBufferSize)); if (!payload->Data) { - interface.Close(payload); + iface.Close(payload); return nullptr; } - IOStream* stream = IOFromInterface(&interface, payload); + IOStream* stream = IOFromInterface(&iface, payload); if (!stream) { - interface.Close(payload); + iface.Close(payload); } return stream; } diff --git a/Juliet/src/Graphics/D3D12/D3D12Shader.cpp b/Juliet/src/Graphics/D3D12/D3D12Shader.cpp index 836c9db..d278d3c 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Shader.cpp +++ b/Juliet/src/Graphics/D3D12/D3D12Shader.cpp @@ -1,16 +1,54 @@ #include +#include #include namespace Juliet::D3D12 { namespace { - void CompileShader(const char* shaderFile) {} + struct D3D12Shader + { + ByteBuffer ByteCode; + }; } // namespace - void LoadShader(const char* shaderFile) + Shader* CreateShader(NonNullPtr driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo) { - CompileShader(shaderFile); + if (!IsValid(shaderByteCode)) + { + LogError(LogCategory::Graphics, "Invalid shader byte code"); + return nullptr; + } + + size_t allocSize = sizeof(D3D12Shader) + shaderByteCode.Size; + auto shader = static_cast(Malloc(allocSize)); + if (!shader) + { + LogError(LogCategory::Graphics, "Cannot allocate a new D3D12Shader: Out of memory"); + return nullptr; + } + + // Uses the bytes after the struct to store the shader byte code. + // TODO:MemoryArena Will use memory arena later that will do the same thing + shader->ByteCode.Data = reinterpret_cast(shader + 1); + shader->ByteCode.Size = shaderByteCode.Size; + MemCopy(shader->ByteCode.Data, shaderByteCode.Data, shaderByteCode.Size); + + // Make sure the data is correctly copied + if (MemCompare(shader->ByteCode.Data, shaderByteCode.Data, shaderByteCode.Size) != 0) + { + LogError(LogCategory::Graphics, "Memory copy failed"); + Free(shader); + return nullptr; + } + + return reinterpret_cast(shader); + } + + void DestroyShader(NonNullPtr driver, NonNullPtr shader) + { + auto d3d12shader = reinterpret_cast(shader.Get()); + Free(d3d12shader); } } // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/D3D12Shader.h b/Juliet/src/Graphics/D3D12/D3D12Shader.h index 278479e..37d7939 100644 --- a/Juliet/src/Graphics/D3D12/D3D12Shader.h +++ b/Juliet/src/Graphics/D3D12/D3D12Shader.h @@ -1,6 +1,11 @@ #pragma once +#include +#include +#include + namespace Juliet::D3D12 { - extern void LoadShader(const char* shaderFile); -} + extern Shader* CreateShader(NonNullPtr driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo); + extern void DestroyShader(NonNullPtr driver, NonNullPtr shader); +} // namespace Juliet::D3D12 diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp index ea8a874..f405818 100644 --- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp +++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp @@ -618,6 +618,9 @@ namespace Juliet::D3D12 device->QueryFence = QueryFence; device->ReleaseFence = ReleaseFence; + device->CreateShader = CreateShader; + device->DestroyShader = DestroyShader; + device->Driver = driver; driver->GraphicsDevice = device; diff --git a/Juliet/src/Graphics/Graphics.cpp b/Juliet/src/Graphics/Graphics.cpp index ad65e68..3ef6b47 100644 --- a/Juliet/src/Graphics/Graphics.cpp +++ b/Juliet/src/Graphics/Graphics.cpp @@ -1,6 +1,7 @@ -#include #include +#include +#include #include #include @@ -175,4 +176,41 @@ namespace Juliet commandListHeader->Device->SetStencilReference(commandList, reference); } + // Shaders + Shader* CreateShader(NonNullPtr device, String filename, ShaderCreateInfo& shaderCreateInfo) + { + ByteBuffer shaderByteCode = {}; + + // Create path from filename + if (IsAbsolutePath(filename)) + { + shaderByteCode = LoadFile(filename); + } + else + { + // TODO: Add path builder in the lib + String base = GetBasePath(); + char inplaceBuffer[256]; + snprintf(inplaceBuffer, sizeof(inplaceBuffer), "%s%s", base.Data, filename.Data); + String absolutePath = WrapString(inplaceBuffer); + shaderByteCode = LoadFile(absolutePath); + } + + if (!IsValid(shaderByteCode)) + { + return nullptr; + } + + Shader* shader = device->CreateShader(device->Driver, shaderByteCode, shaderCreateInfo); + + Free(shaderByteCode); + + return shader; + } + + void DestroyShader(NonNullPtr device, NonNullPtr shader) + { + device->DestroyShader(device->Driver, shader); + } + } // namespace Juliet diff --git a/Juliet/src/Graphics/GraphicsDevice.h b/Juliet/src/Graphics/GraphicsDevice.h index 0311c7a..2615fce 100644 --- a/Juliet/src/Graphics/GraphicsDevice.h +++ b/Juliet/src/Graphics/GraphicsDevice.h @@ -64,6 +64,10 @@ namespace Juliet bool (*QueryFence)(NonNullPtr driver, NonNullPtr fence); void (*ReleaseFence)(NonNullPtr driver, NonNullPtr fence); + // Shaders + Shader* (*CreateShader)(NonNullPtr driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo); + void (*DestroyShader)(NonNullPtr driver, NonNullPtr shader); + const char* Name = "Unknown"; GPUDriver* Driver = nullptr; }; diff --git a/JulietApp/main.cpp b/JulietApp/main.cpp index 12d06d1..bbca511 100644 --- a/JulietApp/main.cpp +++ b/JulietApp/main.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -59,6 +58,20 @@ void JulietApplication::Init() { AttachToWindow(GraphicsDevice, MainWindow); + { + // Create graphics pipeline + // TODO: Assets management that handles path to assets or something. + String shaderPath = WrapString("../../../Assets/compiled/Triangle.vert.dxil"); + ShaderCreateInfo shaderCI = {}; + shaderCI.Stage = ShaderStage::Vertex; + shaderCI.EntryPoint = WrapString("main"); + Shader* shader = CreateShader(GraphicsDevice, shaderPath, shaderCI); + if (shader) + { + DestroyShader(GraphicsDevice, shader); + } + } + GameCode.Functions = reinterpret_cast(&Game); GameCode.FunctionCount = ArraySize(GameFunctionTable); GameCode.FunctionNames = GameFunctionTable; @@ -167,12 +180,14 @@ JulietApplication& GetEditorApplication() int main(int argc, char** argv) { - CreateMutex(0, false, L"Local\\Juliet.App"); - if (GetLastError() == ERROR_ALREADY_EXISTS) - { - MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION); - return EXIT_FAILURE; - } + // Allow only one instance to be launched. + // Need to create Mutex code in the lib because i dont want to include windows.h in this file anymore + // CreateMutex(0, false, L"Local\\Juliet.App"); + // if (GetLastError() == ERROR_ALREADY_EXISTS) + // { + // MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION); + // return EXIT_FAILURE; + // } StartApplication(EditorApplication, JulietInit_Flags::Display);