Shader: Added code to load compiled shader

This commit is contained in:
2025-03-09 15:54:13 -04:00
parent da203c80f3
commit 8c6c42e123
15 changed files with 209 additions and 27 deletions

View File

@@ -145,6 +145,7 @@
<ClInclude Include="include\Graphics\Graphics.h"/>
<ClInclude Include="include\Graphics\GraphicsConfig.h"/>
<ClInclude Include="include\Graphics\RenderPass.h"/>
<ClInclude Include="include\Graphics\Shader.h" />
<ClInclude Include="include\Graphics\Texture.h"/>
<ClInclude Include="include\Juliet.h"/>
<ClInclude Include="include\pch.h"/>

View File

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

View File

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

View File

@@ -1,10 +1,12 @@
#pragma once
#include <Core/Common/NonNullPtr.h>
#include <Core/Common/String.h>
#include <Core/HAL/Display/Display.h>
#include <Core/Math/Shape.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/RenderPass.h>
#include <Graphics/Shader.h>
#include <Juliet.h>
// Graphics Interface
@@ -104,4 +106,8 @@ namespace Juliet
extern JULIET_API void SetScissorRect(NonNullPtr<RenderPass> renderPass, const Rectangle& rectangle);
extern JULIET_API void SetBlendConstants(NonNullPtr<RenderPass> renderPass, FColor blendConstants);
extern JULIET_API void SetStencilReference(NonNullPtr<RenderPass> renderPass, uint8 reference);
// Shaders
extern JULIET_API Shader* CreateShader(NonNullPtr<GraphicsDevice> device, String filename, ShaderCreateInfo& shaderCreateInfo);
extern JULIET_API void DestroyShader(NonNullPtr<GraphicsDevice> driver, NonNullPtr<Shader> shader);
} // namespace Juliet

View File

@@ -0,0 +1,23 @@
#pragma once
#include <Core/Common/String.h>
namespace Juliet
{
// Opaque type
struct Shader;
enum class ShaderStage : uint8
{
Vertex,
Fragment,
Compute
};
struct ShaderCreateInfo
{
ShaderStage Stage;
String EntryPoint;
};
} // namespace Juliet

View File

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

View File

@@ -3,4 +3,5 @@
namespace Juliet::Platform
{
extern String GetBasePath();
}
extern bool IsAbsolutePath(String path);
} // namespace Juliet::Platform

View File

@@ -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<char*>(Calloc(MAX_PATH, sizeof(char)));
size_t bufferSize = MAX_PATH;
auto buffer = static_cast<char*>(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

View File

@@ -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<char*>(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;
}

View File

@@ -1,16 +1,54 @@
#include <pch.h>
#include <Core/Memory/Allocator.h>
#include <Graphics/D3D12/D3D12Shader.h>
namespace Juliet::D3D12
{
namespace
{
void CompileShader(const char* shaderFile) {}
struct D3D12Shader
{
ByteBuffer ByteCode;
};
} // namespace
void LoadShader(const char* shaderFile)
Shader* CreateShader(NonNullPtr<GPUDriver> 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<D3D12Shader*>(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<Byte*>(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*>(shader);
}
void DestroyShader(NonNullPtr<GPUDriver> driver, NonNullPtr<Shader> shader)
{
auto d3d12shader = reinterpret_cast<D3D12Shader*>(shader.Get());
Free(d3d12shader);
}
} // namespace Juliet::D3D12

View File

@@ -1,6 +1,11 @@
#pragma once
#include <Core/Common/NonNullPtr.h>
#include <Graphics/GraphicsDevice.h>
#include <Graphics/Shader.h>
namespace Juliet::D3D12
{
extern void LoadShader(const char* shaderFile);
}
extern Shader* CreateShader(NonNullPtr<GPUDriver> driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo);
extern void DestroyShader(NonNullPtr<GPUDriver> driver, NonNullPtr<Shader> shader);
} // namespace Juliet::D3D12

View File

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

View File

@@ -1,6 +1,7 @@
#include <DX12CommandList.h>
#include <pch.h>
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/HAL/IO/IOStream.h>
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsDevice.h>
@@ -175,4 +176,41 @@ namespace Juliet
commandListHeader->Device->SetStencilReference(commandList, reference);
}
// Shaders
Shader* CreateShader(NonNullPtr<GraphicsDevice> 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<GraphicsDevice> device, NonNullPtr<Shader> shader)
{
device->DestroyShader(device->Driver, shader);
}
} // namespace Juliet

View File

@@ -64,6 +64,10 @@ namespace Juliet
bool (*QueryFence)(NonNullPtr<GPUDriver> driver, NonNullPtr<Fence> fence);
void (*ReleaseFence)(NonNullPtr<GPUDriver> driver, NonNullPtr<Fence> fence);
// Shaders
Shader* (*CreateShader)(NonNullPtr<GPUDriver> driver, ByteBuffer shaderByteCode, ShaderCreateInfo& shaderCreateInfo);
void (*DestroyShader)(NonNullPtr<GPUDriver> driver, NonNullPtr<Shader> shader);
const char* Name = "Unknown";
GPUDriver* Driver = nullptr;
};

View File

@@ -10,7 +10,6 @@
#include <Graphics/Graphics.h>
#include <Graphics/GraphicsConfig.h>
#include <Graphics/RenderPass.h>
#include <Windows.h>
#include <Core/Common/String.h>
#include <Core/Memory/Utils.h>
@@ -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<void**>(&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);