From cd9f0716af1b2184c5a185555bee6d11f04f330e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 31 Jul 2024 15:52:28 +0200 Subject: [PATCH 01/93] Start mesh --- samples/06-HelloMesh/CMakeLists.txt | 10 + samples/06-HelloMesh/Data/RenderMesh.hlsl | 46 ++++ samples/06-HelloMesh/main.c | 320 ++++++++++++++++++++++ samples/Common/SampleUtils.h | 7 +- src/Elemental/Elemental.h | 1 + tests/GraphicsTests/ResourceTests.cpp | 2 + 6 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 samples/06-HelloMesh/CMakeLists.txt create mode 100644 samples/06-HelloMesh/Data/RenderMesh.hlsl create mode 100644 samples/06-HelloMesh/main.c diff --git a/samples/06-HelloMesh/CMakeLists.txt b/samples/06-HelloMesh/CMakeLists.txt new file mode 100644 index 00000000..8cda498c --- /dev/null +++ b/samples/06-HelloMesh/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SAMPLE_NAME HelloMesh) + +#"https://casual-effects.com/g3d/data10/research/model/buddha/buddha.zip" + +add_executable(${SAMPLE_NAME} main.c Data) +target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) +target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) + +configure_resource_compilation(${SAMPLE_NAME} resource_list) +configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/06-HelloMesh/Data/RenderMesh.hlsl b/samples/06-HelloMesh/Data/RenderMesh.hlsl new file mode 100644 index 00000000..850422b7 --- /dev/null +++ b/samples/06-HelloMesh/Data/RenderMesh.hlsl @@ -0,0 +1,46 @@ +struct ShaderParameters +{ + uint32_t VertexBufferIndex; +}; + +[[vk::push_constant]] +ShaderParameters parameters : register(b0); + +struct VertexOutput +{ + float4 Position: SV_Position; +}; + +static uint3 quadIndices[] = +{ + uint3(0, 1, 2), + uint3(2, 1, 3) +}; + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(32, 1, 1)] +void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) +{ + const uint meshVertexCount = 3; + const uint triangleCount = 1; + + SetMeshOutputCounts(meshVertexCount, triangleCount); + + if (groupThreadId < meshVertexCount) + { + ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; + vertices[groupThreadId].Position = float4(vertexBuffer.Load(groupThreadId * sizeof(float3)), 1.0); + } + + if (groupThreadId < triangleCount) + { + indices[groupThreadId] = quadIndices[groupThreadId]; + } +} + +[shader("pixel")] +float4 PixelMain(const VertexOutput input) : SV_Target0 +{ + return float4(1.0, 1.0, 0.0, 1.0); +} diff --git a/samples/06-HelloMesh/main.c b/samples/06-HelloMesh/main.c new file mode 100644 index 00000000..f0e72a4d --- /dev/null +++ b/samples/06-HelloMesh/main.c @@ -0,0 +1,320 @@ +#include +#include "Elemental.h" +#include "SampleUtils.h" +#include "SampleMath.h" +#include "SampleInputs.h" + +#define ROTATION_TOUCH_SPEED 0.5f +#define ROTATION_TOUCH_MAX_DELTA 0.3f +#define ROTATION_MULTITOUCH_SPEED 100.0f +#define ROTATION_ACCELERATION 50.0f +#define ROTATION_FRICTION 40.0f +#define ZOOM_MULTITOUCH_SPEED 100.0f +#define ZOOM_SPEED 0.5f +#define ZOOM_FACTOR 10.0f + +typedef struct +{ + uint32_t VertexBuffer; + SampleMatrix3x3 Transform; +} ShaderParameters; + +typedef struct +{ + SampleVector2 TranslationDelta; + float RotationDelta; + SampleVector2 RotationTouch; + SampleVector2 CurrentTranslationSpeed; + float PreviousTouchDistance; + float PreviousTouchAngle; + float Zoom; +} GameState; + +typedef struct +{ + ElemGraphicsResource VertexBuffer; + ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; +} MeshData; + +// TODO: Group common variables into separate structs +typedef struct +{ + bool PreferVulkan; + ElemWindow Window; + ElemGraphicsDevice GraphicsDevice; + ElemCommandQueue CommandQueue; + ElemGraphicsHeap GraphicsHeap; + uint32_t CurrentHeapOffset; + ElemFence LastExecutionFence; + ElemSwapChain SwapChain; + ElemPipelineState GraphicsPipeline; + ShaderParameters ShaderParameters; + SampleStandardInputActions InputActions; + SampleInputActionBindingSpan InputActionBindings; + GameState GameState; + MeshData TestMeshData; +} ApplicationPayload; + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); + +void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) +{ + uint32_t bufferSize = 10 * sizeof(SampleVector3); + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, bufferSize, ElemGraphicsResourceUsage_Read, NULL); + meshData->VertexBuffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); + applicationPayload->CurrentHeapOffset += SampleAlignValue(bufferDescription.SizeInBytes, bufferDescription.Alignment); + meshData->VertexBufferReadDescriptor = ElemCreateGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBuffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + + float vertices[] = + { + -1.0f, 1.0f, 0.0f, + 0.0f, 1.0f, 0.0f, + -1.0f, 0.0f, 0.0f + }; + + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->TestMeshData.VertexBuffer); + memcpy(vertexBufferPointer.Items, vertices, sizeof(vertices)); +} + +void InitSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + applicationPayload->Window = ElemCreateWindow(NULL); + + ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->PreferVulkan }); + applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); + + applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); + applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); + ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); + + // TODO: For now we need to put the as GpuUpload but it should be Gpu when we use IOQueues + applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + + LoadMesh(&applicationPayload->TestMeshData, "Pouet.mesh", applicationPayload); + applicationPayload->ShaderParameters.VertexBuffer = applicationPayload->TestMeshData.VertexBufferReadDescriptor; + + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); + + applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { + .DebugName = "RenderMesh PSO", + .ShaderLibrary = shaderLibrary, + .MeshShaderFunction = "MeshMain", + .PixelShaderFunction = "PixelMain", + .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + }); + + ElemFreeShaderLibrary(shaderLibrary); + + applicationPayload->ShaderParameters.Transform = SampleCreateIdentityMatrix(); + applicationPayload->InputActions.ShowCursor = true; + applicationPayload->GameState.Zoom = 1.0f; + applicationPayload->GameState.RotationDelta = 0.0f; + + SampleRegisterStandardInputBindings(&applicationPayload->InputActionBindings, &applicationPayload->InputActions); + + SampleStartFrameMeasurement(); +} + +void FreeSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); + + ElemFreePipelineState(applicationPayload->GraphicsPipeline); + ElemFreeSwapChain(applicationPayload->SwapChain); + ElemFreeCommandQueue(applicationPayload->CommandQueue); + + // Free Mesh + ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->TestMeshData.VertexBuffer, NULL); + + ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); + ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); +} + +void ResetTouchParameters(GameState* gameState) +{ + gameState->RotationTouch = V2Zero; + gameState->PreviousTouchDistance = 0.0f; + gameState->PreviousTouchAngle = 0.0f; +} + +void UpdateGameState(GameState* gameState, SampleStandardInputActions* inputActions, float deltaTimeInSeconds) +{ + gameState->TranslationDelta = V2Zero; + + float zoomDelta = 0.0f; + float rotationDeltaZ = 0.0f; + + if (inputActions->Touch) + { + if (inputActions->Touch2) + { + SampleVector2 touchPosition = (SampleVector2) { inputActions->TouchPositionX, inputActions->TouchPositionY }; + SampleVector2 touchPosition2 = (SampleVector2) { inputActions->Touch2PositionX, inputActions->Touch2PositionY }; + + SampleVector2 diffVector = SampleSubstractV2(touchPosition, touchPosition2); + float distance = SampleMagnitudeV2(diffVector); + float angle = atan2(diffVector.X, diffVector.Y); + + if (gameState->PreviousTouchDistance != 0.0f) + { + zoomDelta = -(distance - gameState->PreviousTouchDistance) * ZOOM_MULTITOUCH_SPEED * deltaTimeInSeconds; + } + + if (gameState->PreviousTouchAngle != 0.0f) + { + rotationDeltaZ = -SampleNormalizeAngle(angle - gameState->PreviousTouchAngle) * ROTATION_MULTITOUCH_SPEED * deltaTimeInSeconds; + } + + gameState->PreviousTouchDistance = distance; + gameState->PreviousTouchAngle = angle; + } + else + { + ResetTouchParameters(gameState); + + gameState->TranslationDelta.X = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->TranslationDelta.Y = (inputActions->TouchTranslateUp - inputActions->TouchTranslateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + } + else if (inputActions->TouchRotateSide) + { + ResetTouchParameters(gameState); + rotationDeltaZ = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + else if (inputActions->TouchReleased && !inputActions->Touch2) + { + ResetTouchParameters(gameState); + + gameState->RotationTouch.X = (inputActions->TouchTranslateUp - inputActions->TouchTranslateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationTouch.Y = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + else + { + SampleVector2 direction = SampleNormalizeV2((SampleVector2) + { + .X = (inputActions->TranslateRight - inputActions->TranslateLeft), + .Y = (inputActions->TranslateDown - inputActions->TranslateUp), + }); + + if (SampleMagnitudeSquaredV2(direction)) + { + SampleVector2 acceleration = SampleAddV2(SampleMulScalarV2(direction, ROTATION_ACCELERATION), SampleMulScalarV2(SampleInverseV2(gameState->CurrentTranslationSpeed), ROTATION_FRICTION)); + + ResetTouchParameters(gameState); + + gameState->TranslationDelta = SampleAddV2(SampleMulScalarV2(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV2(gameState->CurrentTranslationSpeed, deltaTimeInSeconds)); + gameState->CurrentTranslationSpeed = SampleAddV2(SampleMulScalarV2(acceleration, deltaTimeInSeconds), gameState->CurrentTranslationSpeed); + } + + rotationDeltaZ = (inputActions->RotateSideLeft - inputActions->RotateSideRight) * 1 * deltaTimeInSeconds; + zoomDelta = -(inputActions->ZoomIn - inputActions->ZoomOut) * ZOOM_SPEED * deltaTimeInSeconds; + } + + if (zoomDelta != 0) + { + if (zoomDelta > 0) + { + gameState->Zoom *= powf(ZOOM_FACTOR, zoomDelta); + } + else + { + gameState->Zoom /= powf(ZOOM_FACTOR, -zoomDelta); + } + } + + gameState->TranslationDelta = SampleMulScalarV2(gameState->TranslationDelta, gameState->Zoom); + gameState->RotationDelta = rotationDeltaZ; +} + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + SampleStandardInputActions* inputActions = &applicationPayload->InputActions; + SampleUpdateInputActions(&applicationPayload->InputActionBindings); + + if (inputActions->ExitApp) + { + ElemExitApplication(0); + } + + if (inputActions->ShowCursor) + { + ElemShowWindowCursor(applicationPayload->Window); + } + else + { + ElemHideWindowCursor(applicationPayload->Window); + } + + GameState* gameState = &applicationPayload->GameState; + UpdateGameState(gameState, inputActions, updateParameters->DeltaTimeInSeconds); + + SampleMatrix3x3 transformMatrix = SampleMulMatrix3x3(SampleCreateTranslationMatrix(gameState->TranslationDelta.X, gameState->TranslationDelta.Y), SampleCreateRotationMatrix(gameState->RotationDelta)); + + applicationPayload->ShaderParameters.Transform = SampleMulMatrix3x3(applicationPayload->ShaderParameters.Transform, transformMatrix); + + ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + + ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) { + .RenderTargets = + { + .Items = (ElemRenderPassRenderTarget[]) { + { + .RenderTarget = updateParameters->BackBufferRenderTarget, + .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }}, + .Length = 1 + } + }); + + ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); + ElemDispatchMesh(commandList, 1, 1, 1); + + ElemEndRenderPass(commandList); + + ElemCommitCommandList(commandList); + applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); + + ElemPresentSwapChain(applicationPayload->SwapChain); + SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); + + if (frameMeasurement.HasNewData) + { + SampleSetWindowTitle(applicationPayload->Window, "HelloMesh", applicationPayload->GraphicsDevice, frameMeasurement.FrameTimeInSeconds, frameMeasurement.Fps); + } + + SampleStartFrameMeasurement(); +} + +int main(int argc, const char* argv[]) +{ + bool preferVulkan = false; + + if (argc > 1 && strcmp(argv[1], "--vulkan") == 0) + { + preferVulkan = true; + } + + ElemConfigureLogHandler(ElemConsoleLogHandler); + + ApplicationPayload payload = + { + .PreferVulkan = preferVulkan + }; + + ElemRunApplication(&(ElemRunApplicationParameters) + { + .ApplicationName = "Hello Mesh", + .InitHandler = InitSample, + .FreeHandler = FreeSample, + .Payload = &payload + }); +} diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index 184b33b0..4df273d8 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -21,11 +21,16 @@ uint64_t SampleMegaBytesToBytes(uint64_t value) return value * 1024 * 1024; } -uint64_t SampleGygaBytesToBytes(uint64_t value) +uint64_t SampleGigaBytesToBytes(uint64_t value) { return SampleMegaBytesToBytes(value) * 1024; } +uint32_t SampleAlignValue(uint32_t value, uint32_t alignment) +{ + return (value + alignment - 1) & ~(alignment - 1); +} + // ----------------------------------------------------------------------------- // I/O Functions // ----------------------------------------------------------------------------- diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index b9e7db10..758bb15e 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -644,6 +644,7 @@ typedef struct ElemFenceSpan FencesToWait; } ElemFreeGraphicsResourceOptions; +// TODO: Here, we could add options to support StructuredBuffer (we need a different stride for that) typedef struct { uint32_t TextureMipIndex; diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 722f7af2..ffbabb36 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -4,6 +4,8 @@ // TODO: Add a test for texture2D uav + rendertarget // TODO: Add a test for gpupload heap can only be used for buffers +// TODO: Add a test for GetResourcePointer only for GpuUpload +// TODO: Test for structured buffer descriptors // TODO: Test all formats // TODO: Test Texture format required // TODO: Test Format SRGB not allowed to Texture UAV From ae0009f98e8003080a32fe992db65ca7b9d22cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 1 Aug 2024 15:21:29 +0200 Subject: [PATCH 02/93] Refactor samples --- .gitignore | 1 + samples/02-ShaderCompiler/CMakeLists.txt | 6 - samples/Common/SampleUtils.h | 129 +++++++++++++++++- .../01-HelloWindow/CMakeLists.txt | 0 samples/{ => Elemental}/01-HelloWindow/main.c | 0 .../02-HelloTriangle}/CMakeLists.txt | 0 .../02-HelloTriangle}/Data/Triangle.hlsl | 0 .../02-HelloTriangle}/main.c | 0 .../03-HelloInputs}/CMakeLists.txt | 0 .../03-HelloInputs}/Data/Triangle.hlsl | 0 .../03-HelloInputs}/main.c | 0 .../04-HelloCompute}/CMakeLists.txt | 0 .../04-HelloCompute}/Data/Fractal.hlsl | 0 .../04-HelloCompute}/Data/Tonemap.hlsl | 0 .../04-HelloCompute}/main.c | 0 .../05-HelloMesh}/CMakeLists.txt | 1 + .../05-HelloMesh}/Data/RenderMesh.hlsl | 0 .../05-HelloMesh}/main.c | 13 +- .../01-ShaderCompiler/CMakeLists.txt | 9 ++ .../01-ShaderCompiler}/main.c | 102 +------------- .../02-MeshCompiler/CMakeLists.txt | 9 ++ samples/ElementalTools/02-MeshCompiler/main.c | 95 +++++++++++++ samples/SharedData/CMakeLists.txt | 3 + .../01-Foundations/01-HelloWorld.png | Bin .../01-Foundations/02-HelloWindow.png | Bin .../02-Graphics/01-HelloTriangle.png | Bin .../GettingStarted.png | Bin .../Graphics/Vulkan/VulkanSwapChain.cpp | 1 + 28 files changed, 257 insertions(+), 112 deletions(-) delete mode 100644 samples/02-ShaderCompiler/CMakeLists.txt rename samples/{ => Elemental}/01-HelloWindow/CMakeLists.txt (100%) rename samples/{ => Elemental}/01-HelloWindow/main.c (100%) rename samples/{03-HelloTriangle => Elemental/02-HelloTriangle}/CMakeLists.txt (100%) rename samples/{03-HelloTriangle => Elemental/02-HelloTriangle}/Data/Triangle.hlsl (100%) rename samples/{03-HelloTriangle => Elemental/02-HelloTriangle}/main.c (100%) rename samples/{04-HelloInputs => Elemental/03-HelloInputs}/CMakeLists.txt (100%) rename samples/{04-HelloInputs => Elemental/03-HelloInputs}/Data/Triangle.hlsl (100%) rename samples/{04-HelloInputs => Elemental/03-HelloInputs}/main.c (100%) rename samples/{05-HelloCompute => Elemental/04-HelloCompute}/CMakeLists.txt (100%) rename samples/{05-HelloCompute => Elemental/04-HelloCompute}/Data/Fractal.hlsl (100%) rename samples/{05-HelloCompute => Elemental/04-HelloCompute}/Data/Tonemap.hlsl (100%) rename samples/{05-HelloCompute => Elemental/04-HelloCompute}/main.c (100%) rename samples/{06-HelloMesh => Elemental/05-HelloMesh}/CMakeLists.txt (89%) rename samples/{06-HelloMesh => Elemental/05-HelloMesh}/Data/RenderMesh.hlsl (100%) rename samples/{06-HelloMesh => Elemental/05-HelloMesh}/main.c (95%) create mode 100644 samples/ElementalTools/01-ShaderCompiler/CMakeLists.txt rename samples/{02-ShaderCompiler => ElementalTools/01-ShaderCompiler}/main.c (60%) create mode 100644 samples/ElementalTools/02-MeshCompiler/CMakeLists.txt create mode 100644 samples/ElementalTools/02-MeshCompiler/main.c create mode 100644 samples/SharedData/CMakeLists.txt rename samples/{screenshots_old => XX-screenshots_old}/01-Foundations/01-HelloWorld.png (100%) rename samples/{screenshots_old => XX-screenshots_old}/01-Foundations/02-HelloWindow.png (100%) rename samples/{screenshots_old => XX-screenshots_old}/02-Graphics/01-HelloTriangle.png (100%) rename samples/{screenshots_old => XX-screenshots_old}/GettingStarted.png (100%) diff --git a/.gitignore b/.gitignore index a6d15501..6ac79a39 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ *.userosscache *.sln.docstates *.resolved +*.obj xcuserdata/ packages/ diff --git a/samples/02-ShaderCompiler/CMakeLists.txt b/samples/02-ShaderCompiler/CMakeLists.txt deleted file mode 100644 index c3c77d5f..00000000 --- a/samples/02-ShaderCompiler/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -if(NOT BUILD_FOR_IOS) - add_executable(ShaderCompiler main.c) - target_link_libraries(ShaderCompiler PRIVATE ElementalToolsInterface) - - configure_project_package(ShaderCompiler "samples" DEPENDENCIES ElementalTools) -endif() diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index 4df273d8..dfa186df 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -1,8 +1,14 @@ #pragma once +#include #include #include -#include "Elemental.h" +#include +#include + +#ifdef ElemToolsAPI +typedef ElemToolsDataSpan ElemDataSpan; +#endif #ifndef _WIN32 #define MAX_PATH 255 @@ -47,7 +53,9 @@ void SampleGetFullPath(char* destination, const char* path) { memset(destination, 0, MAX_PATH); - char* pointer = NULL; + char* pointer = destination; + + #ifdef ElemAPI ElemSystemInfo systemInfo = ElemGetSystemInfo(); CopyString(destination, MAX_PATH, systemInfo.ApplicationPath, strlen(systemInfo.ApplicationPath)); @@ -66,8 +74,9 @@ void SampleGetFullPath(char* destination, const char* path) CopyString(pointer, pointer - destination, folderPrefix, strlen(folderPrefix)); pointer = pointer + strlen(folderPrefix); + #endif - CopyString(pointer, pointer - destination, path, strlen(path)); + CopyString(pointer, MAX_PATH - (int32_t)(pointer - destination), path, strlen(path)); } ElemDataSpan SampleReadFile(const char* filename) @@ -120,6 +129,7 @@ ElemDataSpan SampleReadFile(const char* filename) rewind(file); uint8_t* buffer = (uint8_t*)malloc(fileSize + 1); + memset(buffer, 0, fileSize + 1); if (buffer == NULL) { @@ -155,10 +165,122 @@ ElemDataSpan SampleReadFile(const char* filename) }; } +int SampleWriteDataToFile(const char* filename, ElemDataSpan data) +{ + printf("Length:%s %d\n", filename, data.Length); + if (filename == NULL || data.Length == 0) + { + printf("ERROR 1\n"); + return -1; + } + + #ifdef _WIN32 + FILE* file; + fopen_s(&file, filename, "wb"); + #else + FILE* file = fopen(filename, "wb"); + #endif + + if (file == NULL) + { + printf("ERROR 2\n"); + return -1; + } + + size_t bytesWritten = fwrite(data.Items, 1, data.Length, file); + fclose(file); + + if (bytesWritten < data.Length) + { + printf("ERROR 3\n"); + return -1; // Return -1 if not all bytes were written + } + + return 0; // Success +} + +ElemDataSpan SampleReadLine(ElemDataSpan* data) +{ + for (uint32_t i = 0; i < data->Length; i++) + { + if (data->Items[i] == '\n') + { + ElemDataSpan result = + { + .Items = data->Items, + .Length = i + }; + + data->Items += i + 1; + data->Length -= i + 1; + return result; + } + } + + return (ElemDataSpan){}; +} + +void SamplePrintDataSpan(ElemDataSpan data) +{ + for (uint32_t i = 0; i < data.Length; i++) + { + printf("%c", data.Items[i]); + } + + printf("\n"); +} + +void SampleSplitString(ElemDataSpan* destination, uint32_t* destinationCount, ElemDataSpan source) +{ + uint8_t* pointer = source.Items; + + for (uint32_t i = 0; i < source.Length; i++) + { + if (source.Items[i] == ' ') + { + destination[(*destinationCount)++] = (ElemDataSpan) + { + .Items = pointer, + .Length = (source.Items + i) - pointer + }; + + if (i + 1 < source.Length) + { + pointer = source.Items + i + 1; + } + } + } + + destination[(*destinationCount)++] = (ElemDataSpan) + { + .Items = pointer, + .Length = (source.Items + source.Length) - pointer + }; +} + +bool SampleCompareString(ElemDataSpan data, const char* value) +{ + if (strlen(value) != data.Length) + { + return false; + } + + for (uint32_t i = 0; i < data.Length; i++) + { + if (data.Items[i] != value[i]) + { + return false; + } + } + + return true; +} + // ----------------------------------------------------------------------------- // UI Functions // ----------------------------------------------------------------------------- +#ifdef ElemAPI const char* SampleGetPlatformLabel(ElemPlatform platform) { switch (platform) @@ -236,6 +358,7 @@ void SampleSetWindowTitle(ElemWindow window, const char* applicationName, ElemGr memoryFormatted); ElemSetWindowTitle(window, titleFormatted); } +#endif // ----------------------------------------------------------------------------- // Timing Functions diff --git a/samples/01-HelloWindow/CMakeLists.txt b/samples/Elemental/01-HelloWindow/CMakeLists.txt similarity index 100% rename from samples/01-HelloWindow/CMakeLists.txt rename to samples/Elemental/01-HelloWindow/CMakeLists.txt diff --git a/samples/01-HelloWindow/main.c b/samples/Elemental/01-HelloWindow/main.c similarity index 100% rename from samples/01-HelloWindow/main.c rename to samples/Elemental/01-HelloWindow/main.c diff --git a/samples/03-HelloTriangle/CMakeLists.txt b/samples/Elemental/02-HelloTriangle/CMakeLists.txt similarity index 100% rename from samples/03-HelloTriangle/CMakeLists.txt rename to samples/Elemental/02-HelloTriangle/CMakeLists.txt diff --git a/samples/03-HelloTriangle/Data/Triangle.hlsl b/samples/Elemental/02-HelloTriangle/Data/Triangle.hlsl similarity index 100% rename from samples/03-HelloTriangle/Data/Triangle.hlsl rename to samples/Elemental/02-HelloTriangle/Data/Triangle.hlsl diff --git a/samples/03-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c similarity index 100% rename from samples/03-HelloTriangle/main.c rename to samples/Elemental/02-HelloTriangle/main.c diff --git a/samples/04-HelloInputs/CMakeLists.txt b/samples/Elemental/03-HelloInputs/CMakeLists.txt similarity index 100% rename from samples/04-HelloInputs/CMakeLists.txt rename to samples/Elemental/03-HelloInputs/CMakeLists.txt diff --git a/samples/04-HelloInputs/Data/Triangle.hlsl b/samples/Elemental/03-HelloInputs/Data/Triangle.hlsl similarity index 100% rename from samples/04-HelloInputs/Data/Triangle.hlsl rename to samples/Elemental/03-HelloInputs/Data/Triangle.hlsl diff --git a/samples/04-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c similarity index 100% rename from samples/04-HelloInputs/main.c rename to samples/Elemental/03-HelloInputs/main.c diff --git a/samples/05-HelloCompute/CMakeLists.txt b/samples/Elemental/04-HelloCompute/CMakeLists.txt similarity index 100% rename from samples/05-HelloCompute/CMakeLists.txt rename to samples/Elemental/04-HelloCompute/CMakeLists.txt diff --git a/samples/05-HelloCompute/Data/Fractal.hlsl b/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl similarity index 100% rename from samples/05-HelloCompute/Data/Fractal.hlsl rename to samples/Elemental/04-HelloCompute/Data/Fractal.hlsl diff --git a/samples/05-HelloCompute/Data/Tonemap.hlsl b/samples/Elemental/04-HelloCompute/Data/Tonemap.hlsl similarity index 100% rename from samples/05-HelloCompute/Data/Tonemap.hlsl rename to samples/Elemental/04-HelloCompute/Data/Tonemap.hlsl diff --git a/samples/05-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c similarity index 100% rename from samples/05-HelloCompute/main.c rename to samples/Elemental/04-HelloCompute/main.c diff --git a/samples/06-HelloMesh/CMakeLists.txt b/samples/Elemental/05-HelloMesh/CMakeLists.txt similarity index 89% rename from samples/06-HelloMesh/CMakeLists.txt rename to samples/Elemental/05-HelloMesh/CMakeLists.txt index 8cda498c..a670411f 100644 --- a/samples/06-HelloMesh/CMakeLists.txt +++ b/samples/Elemental/05-HelloMesh/CMakeLists.txt @@ -5,6 +5,7 @@ set(SAMPLE_NAME HelloMesh) add_executable(${SAMPLE_NAME} main.c Data) target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) +add_dependencies(${SAMPLE_NAME} SampleSharedData) configure_resource_compilation(${SAMPLE_NAME} resource_list) configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl similarity index 100% rename from samples/06-HelloMesh/Data/RenderMesh.hlsl rename to samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl diff --git a/samples/06-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c similarity index 95% rename from samples/06-HelloMesh/main.c rename to samples/Elemental/05-HelloMesh/main.c index f0e72a4d..7fc0b977 100644 --- a/samples/06-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -59,11 +59,12 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) { - uint32_t bufferSize = 10 * sizeof(SampleVector3); - ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, bufferSize, ElemGraphicsResourceUsage_Read, NULL); + // TODO: Alignment should be used with the offset before adding the size of the resource! + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, 10 * sizeof(SampleVector3), ElemGraphicsResourceUsage_Read, NULL); + + applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); meshData->VertexBuffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); - applicationPayload->CurrentHeapOffset += SampleAlignValue(bufferDescription.SizeInBytes, bufferDescription.Alignment); - meshData->VertexBufferReadDescriptor = ElemCreateGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBuffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; float vertices[] = { @@ -74,6 +75,8 @@ void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicat ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->TestMeshData.VertexBuffer); memcpy(vertexBufferPointer.Items, vertices, sizeof(vertices)); + + meshData->VertexBufferReadDescriptor = ElemCreateGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBuffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); } void InitSample(void* payload) @@ -88,7 +91,7 @@ void InitSample(void* payload) applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); - // TODO: For now we need to put the as GpuUpload but it should be Gpu when we use IOQueues + // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); LoadMesh(&applicationPayload->TestMeshData, "Pouet.mesh", applicationPayload); diff --git a/samples/ElementalTools/01-ShaderCompiler/CMakeLists.txt b/samples/ElementalTools/01-ShaderCompiler/CMakeLists.txt new file mode 100644 index 00000000..1df1e895 --- /dev/null +++ b/samples/ElementalTools/01-ShaderCompiler/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT BUILD_FOR_IOS) + set(SAMPLE_NAME ShaderCompiler) + + add_executable(${SAMPLE_NAME} main.c) + target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) + target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalToolsInterface) + + configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES ElementalTools) +endif() diff --git a/samples/02-ShaderCompiler/main.c b/samples/ElementalTools/01-ShaderCompiler/main.c similarity index 60% rename from samples/02-ShaderCompiler/main.c rename to samples/ElementalTools/01-ShaderCompiler/main.c index aeedbb47..75dc8519 100644 --- a/samples/02-ShaderCompiler/main.c +++ b/samples/ElementalTools/01-ShaderCompiler/main.c @@ -1,99 +1,5 @@ #include "ElementalTools.h" -#include - -#ifndef _WIN32 -#define MAX_PATH 255 -#include -#else -#include -#endif - -char* ReadFileToString(const char* filename) -{ - #ifdef _WIN32 - FILE* file; - fopen_s(&file, filename, "rb"); - #else - FILE* file = fopen(filename, "rb"); - #endif - - if (file == NULL) - { - return NULL; - } - - if (fseek(file, 0, SEEK_END) != 0) - { - fclose(file); - return NULL; - } - - long fileSize = ftell(file); - - if (fileSize == -1) - { - fclose(file); - return NULL; - } - - rewind(file); - - char* buffer = (char*)malloc(fileSize + 1); - - if (buffer == NULL) - { - fclose(file); - return NULL; - } - - size_t bytesRead = fread(buffer, 1, fileSize, file); - - if (bytesRead < (size_t)fileSize) - { - free(buffer); - fclose(file); - return NULL; - } - - buffer[fileSize] = '\0'; - fclose(file); - - return buffer; -} - -int WriteDataToFile(const char* filename, ElemToolsDataSpan data) -{ - printf("Length:%s %d\n", filename, data.Length); - if (filename == NULL || data.Length == 0) - { - printf("ERROR 1\n"); - return -1; - } - - #ifdef _WIN32 - FILE* file; - fopen_s(&file, filename, "wb"); - #else - FILE* file = fopen(filename, "wb"); - #endif - - if (file == NULL) - { - printf("ERROR 2\n"); - return -1; - } - - size_t bytesWritten = fwrite(data.Items, 1, data.Length, file); - fclose(file); - - if (bytesWritten < data.Length) - { - printf("ERROR 3\n"); - return -1; // Return -1 if not all bytes were written - } - - return 0; // Success -} +#include "SampleUtils.h" int main(int argc, const char* argv[]) { @@ -164,8 +70,8 @@ int main(int argc, const char* argv[]) printf("Compiling shader: %s (DebugMode=%d)\n", inputPath, debugMode); - char* shaderSource = ReadFileToString(inputPath); - ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = { .Items = (uint8_t*)shaderSource, .Length = strlen(shaderSource) } }; + ElemToolsDataSpan shaderSource = SampleReadFile(inputPath); + ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = shaderSource }; ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary(targetApi, targetPlatform, &shaderSourceData, &(ElemCompileShaderOptions) { .DebugMode = debugMode }); for (uint32_t i = 0; i < compilationResult.Messages.Length; i++) @@ -180,5 +86,5 @@ int main(int argc, const char* argv[]) } printf("Writing shader data to: %s\n", outputPath); - return WriteDataToFile(outputPath, compilationResult.Data); + return SampleWriteDataToFile(outputPath, compilationResult.Data); } diff --git a/samples/ElementalTools/02-MeshCompiler/CMakeLists.txt b/samples/ElementalTools/02-MeshCompiler/CMakeLists.txt new file mode 100644 index 00000000..dbf60bcf --- /dev/null +++ b/samples/ElementalTools/02-MeshCompiler/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT BUILD_FOR_IOS) + set(SAMPLE_NAME MeshCompiler) + + add_executable(${SAMPLE_NAME} main.c) + target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) + target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalToolsInterface) + + configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES ElementalTools) +endif() diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c new file mode 100644 index 00000000..18c9ca3d --- /dev/null +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -0,0 +1,95 @@ +#include "ElementalTools.h" +#include "SampleUtils.h" + +void SampleReadObjMesh(ElemToolsDataSpan data) +{ + ElemToolsDataSpan line = SampleReadLine(&data); + + while (line.Length > 0) + { + ElemToolsDataSpan lineParts[64]; + uint32_t linePartCount = 0; + SampleSplitString(lineParts, &linePartCount, line); + + if (linePartCount > 1) + { + if (SampleCompareString(lineParts[0], "g")) + { + printf("Group!\n"); + } + else if (SampleCompareString(lineParts[0], "v")) + { + printf("Vertex!\n"); + } + else if (SampleCompareString(lineParts[0], "vn")) + { + printf("Normal!\n"); + } + else if (SampleCompareString(lineParts[0], "f")) + { + printf("Face!\n"); + } + else + { + printf("Unknown!\n"); + } + } + + line = SampleReadLine(&data); + } +} + +int main(int argc, const char* argv[]) +{ + if (argc < 3) + { + printf("USAGE: MeshCompiler [options] inputfile outputfile\n"); + printf("\n"); + printf("OPTIONS:\n"); + printf(" --meshlet-triangle-count\tTBD: TBD. Default: TBD.\n"); + printf("\n"); + return 0; + } + + int32_t inputPathIndex = argc - 2; + const char* inputPath = argv[inputPathIndex]; + + int32_t outputPathIndex = argc - 1; + const char* outputPath = argv[outputPathIndex]; + + // TODO: Add more checks + for (uint32_t i = 1; i < (uint32_t)(argc - 2); i++) + { + printf("Options: %s\n", argv[i]); + + /*if (strcmp(argv[i], "--target-platform") == 0) + { + const char* targetPlatformString = argv[i + 1]; + + if (strcmp(targetPlatformString, "iOS") == 0) + { + targetPlatform = ElemToolsPlatform_iOS; + printf("iOS platform\n"); + } + } + else if (strcmp(argv[i], "--target-api") == 0) + { + const char* targetPlatformString = argv[i + 1]; + + if (strcmp(targetPlatformString, "vulkan") == 0) + { + targetApi = ElemToolsGraphicsApi_Vulkan; + printf("Vulkan api\n"); + } + } + else if (strcmp(argv[i], "--debug") == 0) + { + debugMode = true; + }*/ + } + + printf("Compiling mesh: %s\n", inputPath); + + ElemToolsDataSpan inputData = SampleReadFile(inputPath); + SampleReadObjMesh(inputData); +} diff --git a/samples/SharedData/CMakeLists.txt b/samples/SharedData/CMakeLists.txt new file mode 100644 index 00000000..bf622930 --- /dev/null +++ b/samples/SharedData/CMakeLists.txt @@ -0,0 +1,3 @@ +add_custom_target(SampleSharedData ALL) + +message("Sample Shared Data") diff --git a/samples/screenshots_old/01-Foundations/01-HelloWorld.png b/samples/XX-screenshots_old/01-Foundations/01-HelloWorld.png similarity index 100% rename from samples/screenshots_old/01-Foundations/01-HelloWorld.png rename to samples/XX-screenshots_old/01-Foundations/01-HelloWorld.png diff --git a/samples/screenshots_old/01-Foundations/02-HelloWindow.png b/samples/XX-screenshots_old/01-Foundations/02-HelloWindow.png similarity index 100% rename from samples/screenshots_old/01-Foundations/02-HelloWindow.png rename to samples/XX-screenshots_old/01-Foundations/02-HelloWindow.png diff --git a/samples/screenshots_old/02-Graphics/01-HelloTriangle.png b/samples/XX-screenshots_old/02-Graphics/01-HelloTriangle.png similarity index 100% rename from samples/screenshots_old/02-Graphics/01-HelloTriangle.png rename to samples/XX-screenshots_old/02-Graphics/01-HelloTriangle.png diff --git a/samples/screenshots_old/GettingStarted.png b/samples/XX-screenshots_old/GettingStarted.png similarity index 100% rename from samples/screenshots_old/GettingStarted.png rename to samples/XX-screenshots_old/GettingStarted.png diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index b47ad190..06428b0c 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -149,6 +149,7 @@ void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t hei swapChainCreateInfo->imageExtent.width = width; swapChainCreateInfo->imageExtent.height = height; + // BUG: The resizing works well but not fullscreen! swapChainData->DeviceObject = CreateVulkanSwapChainObject(swapChainData->CommandQueue, swapChainData->WindowSurface, swapChainCreateInfo, oldSwapChain); for (uint32_t i = 0; i < VULKAN_MAX_SWAPCHAIN_BUFFERS; i++) From 279d65878c4787128c69d5a4b33f0e67a35cbaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 8 Aug 2024 19:35:26 +0200 Subject: [PATCH 03/93] Obj mesh parsing --- external/Vulkan-Headers | 2 +- external/volk | 2 +- samples/Common/SampleMath.h | 3 + samples/Common/SampleUtils.h | 4 +- .../ElementalTools/01-ShaderCompiler/main.c | 7 + samples/ElementalTools/02-MeshCompiler/main.c | 196 +++++++++++++++++- samples/SamplesTODO.md | 1 + src/ElementalTools/ElementalTools.h | 15 +- 8 files changed, 215 insertions(+), 15 deletions(-) diff --git a/external/Vulkan-Headers b/external/Vulkan-Headers index 31aa7f63..577baa05 160000 --- a/external/Vulkan-Headers +++ b/external/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 31aa7f634b052d87ede4664053e85f3f4d1d50d3 +Subproject commit 577baa05033cf1d9236b3d078ca4b3269ed87a2b diff --git a/external/volk b/external/volk index 12e006f6..01986ac8 160000 --- a/external/volk +++ b/external/volk @@ -1 +1 @@ -Subproject commit 12e006f60f6f10bc92205612d6875ee539c354ad +Subproject commit 01986ac85fa2e5c70df09aeae9c907e27c5d50b2 diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 5f1b51cf..247e73c3 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -8,6 +8,9 @@ * on readability and not performance. Don't use it in your production code! */ +// TODO: Review the angle functions, we are using left handed coordinate system. So positive rotation should +// be in clockwise order + float SamplePow2f(float value) { return value * value; diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index dfa186df..819a3ac9 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -230,13 +230,13 @@ void SamplePrintDataSpan(ElemDataSpan data) printf("\n"); } -void SampleSplitString(ElemDataSpan* destination, uint32_t* destinationCount, ElemDataSpan source) +void SampleSplitString(ElemDataSpan* destination, uint32_t* destinationCount, ElemDataSpan source, char separator) { uint8_t* pointer = source.Items; for (uint32_t i = 0; i < source.Length; i++) { - if (source.Items[i] == ' ') + if (source.Items[i] == separator) { destination[(*destinationCount)++] = (ElemDataSpan) { diff --git a/samples/ElementalTools/01-ShaderCompiler/main.c b/samples/ElementalTools/01-ShaderCompiler/main.c index 75dc8519..fc55fab8 100644 --- a/samples/ElementalTools/01-ShaderCompiler/main.c +++ b/samples/ElementalTools/01-ShaderCompiler/main.c @@ -71,6 +71,13 @@ int main(int argc, const char* argv[]) printf("Compiling shader: %s (DebugMode=%d)\n", inputPath, debugMode); ElemToolsDataSpan shaderSource = SampleReadFile(inputPath); + + if (shaderSource.Length == 0) + { + printf("File doesn't exist.\n"); + return 1; + } + ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = shaderSource }; ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary(targetApi, targetPlatform, &shaderSourceData, &(ElemCompileShaderOptions) { .DebugMode = debugMode }); diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index 18c9ca3d..c9ed9266 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -1,46 +1,215 @@ #include "ElementalTools.h" #include "SampleUtils.h" +#include "SampleMath.h" -void SampleReadObjMesh(ElemToolsDataSpan data) +/** + * WARNING: This obj parsing code is just for demonstration purpose only. The parsing is not feature complete + * and the implementation focus on readability and not performance. Don't use it in your production code! + */ + +typedef struct +{ + uint32_t SubObjectCount; + uint32_t VertexCount; + uint32_t NormalCount; + uint32_t TextCoordCount; + uint32_t FaceCount; +} ObjMeshElementCount; + +// TODO: Fix warning +typedef struct +{ + union + { + struct + { + uint32_t VertexIndex; + uint32_t NormalIndex; + uint32_t TextCoordIndex; + }; + + uint32_t Indices[3]; + } Elements[3]; +} ObjMeshFace; + +ObjMeshElementCount CountObjMeshElements(ElemToolsDataSpan data) +{ + ElemToolsDataSpan line = SampleReadLine(&data); + + ObjMeshElementCount result = { .VertexCount = 1, .NormalCount = 1, .TextCoordCount = 1 }; + + while (line.Length > 0) + { + if (line.Length > 2) + { + if (line.Items[0] == 'g') + { + result.SubObjectCount++; + } + else if (line.Items[0] == 'v' && line.Items[1] == 'n') + { + result.NormalCount++; + } + else if (line.Items[0] == 'v' && line.Items[1] == 't') + { + result.TextCoordCount++; + } + else if (line.Items[0] == 'v') + { + result.VertexCount++; + } + else if (line.Items[0] == 'f') + { + result.FaceCount++; + } + } + + line = SampleReadLine(&data); + } + + printf("SubObject: %d\n", result.SubObjectCount); + printf("Vextex: %d\n", result.VertexCount); + printf("Normal: %d\n", result.NormalCount); + printf("TestCoord: %d\n", result.TextCoordCount); + printf("Face: %d\n", result.FaceCount); + + return result; +} + +SampleVector2 ReadObjVector2(ElemToolsDataSpan* lineParts, uint32_t linePartCount) +{ + if (linePartCount < 3) + { + printf("Error: Invalid number of elements in the line for Vector2.\n"); + return (SampleVector2) { 0 }; + } + + float x = atof((const char*)lineParts[1].Items); + float y = atof((const char*)lineParts[2].Items); + + return (SampleVector2) + { + .X = x, + .Y = y + }; +} + +SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCount) { + if (linePartCount < 4) + { + printf("Error: Invalid number of elements in the line for Vector3.\n"); + return (SampleVector3) { 0 }; + } + + float x = atof((const char*)lineParts[1].Items); + float y = atof((const char*)lineParts[2].Items); + float z = atof((const char*)lineParts[3].Items); + + return (SampleVector3) + { + .X = x, + .Y = y, + .Z = z + }; +} + +ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) +{ + if (linePartCount < 4) + { + printf("Error: Invalid number of elements in the line for Face.\n"); + return (ObjMeshFace) { 0 }; + } + + ObjMeshFace result = { 0 }; + + for (uint32_t i = 0; i < 3; i++) + { + ElemToolsDataSpan faceParts[3]; + uint32_t facePartCount = 0; + + SampleSplitString(faceParts, &facePartCount, lineParts[i + 1], '/'); + + for (uint32_t j = 0; j < 3; j++) + { + if (faceParts[j].Length > 0) + { + result.Elements[i].Indices[j] = atoi((const char*)faceParts[j].Items); + } + } + } + + return result; +} + +void ReadObjMesh(ElemToolsDataSpan data) +{ + ObjMeshElementCount meshElementCount = CountObjMeshElements(data); ElemToolsDataSpan line = SampleReadLine(&data); + SampleVector3* vertexList = malloc(meshElementCount.VertexCount * sizeof(SampleVector3)); + uint32_t vertexCount = 1; + + SampleVector3* normalList = malloc(meshElementCount.NormalCount * sizeof(SampleVector3)); + uint32_t normalCount = 1; + + SampleVector2* textCoordList = malloc(meshElementCount.TextCoordCount * sizeof(SampleVector2)); + uint32_t textCoordCount = 1; + + ObjMeshFace* faceList = malloc(meshElementCount.FaceCount * sizeof(ObjMeshFace)); + uint32_t faceCount = 0; + while (line.Length > 0) { ElemToolsDataSpan lineParts[64]; uint32_t linePartCount = 0; - SampleSplitString(lineParts, &linePartCount, line); + SampleSplitString(lineParts, &linePartCount, line, ' '); if (linePartCount > 1) { if (SampleCompareString(lineParts[0], "g")) { - printf("Group!\n"); + printf("ERROR: Group not implemented yet!\n"); } else if (SampleCompareString(lineParts[0], "v")) { - printf("Vertex!\n"); + vertexList[vertexCount++] = ReadObjVector3(lineParts, linePartCount); } else if (SampleCompareString(lineParts[0], "vn")) { - printf("Normal!\n"); + normalList[normalCount++] = ReadObjVector3(lineParts, linePartCount); } - else if (SampleCompareString(lineParts[0], "f")) + else if (SampleCompareString(lineParts[0], "vt")) { - printf("Face!\n"); + textCoordList[textCoordCount++] = ReadObjVector2(lineParts, linePartCount); } - else + else if (SampleCompareString(lineParts[0], "f")) { - printf("Unknown!\n"); + faceList[faceCount++] = ReadObjFace(lineParts, linePartCount); } } line = SampleReadLine(&data); } + + for (uint32_t i = 0; i < 10; i++) + { + printf("Vertex %d: %f, %f, %f\n", i, vertexList[i].X, vertexList[i].Y, vertexList[i].Z); + } + + for (uint32_t i = 0; i < 10; i++) + { + for (uint32_t j = 0; j < 3; j++) + { + printf("Face %d-%d: %d, %d, %d\n", i, j, faceList[i].Elements[j].VertexIndex, faceList[i].Elements[j].NormalIndex, faceList[i].Elements[j].TextCoordIndex); + } + } } int main(int argc, const char* argv[]) { + // TODO: Add an option to handle handness change if (argc < 3) { printf("USAGE: MeshCompiler [options] inputfile outputfile\n"); @@ -91,5 +260,12 @@ int main(int argc, const char* argv[]) printf("Compiling mesh: %s\n", inputPath); ElemToolsDataSpan inputData = SampleReadFile(inputPath); - SampleReadObjMesh(inputData); + + if (inputData.Length == 0) + { + printf("File doesn't exist.\n"); + return 1; + } + + ReadObjMesh(inputData); } diff --git a/samples/SamplesTODO.md b/samples/SamplesTODO.md index d94863a3..168a5b7c 100644 --- a/samples/SamplesTODO.md +++ b/samples/SamplesTODO.md @@ -5,6 +5,7 @@ - Multi viewports sample (like a 3D software) - Frame pacing - Occlusion culling with work graphs + Check https://github.com/zeux/niagara/commit/bf90aa8c78e352d3b753b35553a3bcc8c65ef7a0 for a potential bug if we take code from CoreEngine - Hello Raytracing (Cornel Box) With dispayRays and RayInline - Texture streaming - Upscaling diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index a407ec6f..23fc5277 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -37,7 +37,7 @@ #endif //------------------------------------------------------------------------ -// ##Module_Application## +// ##Module_Tools## //------------------------------------------------------------------------ /** @@ -185,10 +185,23 @@ ElemToolsAPI bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemTo * @param options Compilation options such as debug mode. * @return The result of the compilation, including any binaries and messages. */ +// TODO: Put graphics api and platform into options (default to current one) ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const ElemShaderSourceData* sourceData, const ElemCompileShaderOptions* options); // TODO: Can we compile multiple source files into one library? +typedef struct +{ + ElemToolsDataSpan Data; + uint32_t VertexSize; +} ElemVertexBuffer; + +typedef struct +{ +} ElemBuildMeshletsOptions; + +ElemToolsAPI void ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); + #ifdef UseToolsLoader #ifndef ElementalToolsLoader #include "ElementalToolsLoader.c" From 25e7569ed905a04da4280bce0dfa9e697358b346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 8 Aug 2024 19:36:17 +0200 Subject: [PATCH 04/93] Add MeshOptimizer to external --- .gitmodules | 3 +++ external/meshoptimizer | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/meshoptimizer diff --git a/.gitmodules b/.gitmodules index 38f56fef..ef429247 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "external/metal-cpp"] path = external/metal-cpp url = https://github.com/bkaradzic/metal-cpp.git +[submodule "external/meshoptimizer"] + path = external/meshoptimizer + url = https://github.com/zeux/meshoptimizer.git diff --git a/external/meshoptimizer b/external/meshoptimizer new file mode 160000 index 00000000..2f7663bb --- /dev/null +++ b/external/meshoptimizer @@ -0,0 +1 @@ +Subproject commit 2f7663bb321b6242674165a4feef242139ea85b7 From 7052b0ddf05687a218bc1f12740efc2ce943544a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 12 Aug 2024 07:09:55 +0200 Subject: [PATCH 05/93] Meshlet builder --- .gitignore | 1 + CMakeLists.txt | 2 +- external/CMakeLists.txt | 8 +- samples/Common/SampleUtils.h | 8 +- .../ElementalTools/01-ShaderCompiler/main.c | 2 +- samples/ElementalTools/02-MeshCompiler/main.c | 86 +++++++++++++---- src/ElementalTools/Apple/PreCompiledHeader.h | 1 + src/ElementalTools/Common/MeshletBuilder.cpp | 95 +++++++++++++++++++ src/ElementalTools/Common/UnityBuild.cpp | 2 + src/ElementalTools/ElementalTools.h | 36 ++++++- src/ElementalTools/ElementalToolsLoader.c | 39 +++++++- src/ElementalTools/Linux/PreCompiledHeader.h | 1 + .../Microsoft/PreCompiledHeader.h | 2 + 13 files changed, 256 insertions(+), 27 deletions(-) create mode 100644 src/ElementalTools/Common/MeshletBuilder.cpp diff --git a/.gitignore b/.gitignore index 6ac79a39..580866c9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.sln.docstates *.resolved *.obj +*.mesh xcuserdata/ packages/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 02eb8d91..3fa096f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") -set(CMAKE_C_STANDARD "99") +set(CMAKE_C_STANDARD "23") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 20a48d51..3c7ad4c2 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -193,7 +193,6 @@ if(WIN32) endfunction() endif() - #======================================================================= # Shader Compilers #======================================================================= @@ -204,11 +203,16 @@ if(WIN32 OR APPLE) target_link_libraries(tools_shader_compilers INTERFACE metal-shader-converter) endif() +#======================================================================= +# Mesh Optimizer +#======================================================================= +add_subdirectory(./meshoptimizer) + #======================================================================= # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer) #======================================================================= # xxHash diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index 819a3ac9..9b0a1346 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -165,7 +165,7 @@ ElemDataSpan SampleReadFile(const char* filename) }; } -int SampleWriteDataToFile(const char* filename, ElemDataSpan data) +int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) { printf("Length:%s %d\n", filename, data.Length); if (filename == NULL || data.Length == 0) @@ -217,7 +217,11 @@ ElemDataSpan SampleReadLine(ElemDataSpan* data) } } - return (ElemDataSpan){}; + return (ElemDataSpan) + { + .Items = NULL, + .Length = 0 + }; } void SamplePrintDataSpan(ElemDataSpan data) diff --git a/samples/ElementalTools/01-ShaderCompiler/main.c b/samples/ElementalTools/01-ShaderCompiler/main.c index fc55fab8..8becb299 100644 --- a/samples/ElementalTools/01-ShaderCompiler/main.c +++ b/samples/ElementalTools/01-ShaderCompiler/main.c @@ -93,5 +93,5 @@ int main(int argc, const char* argv[]) } printf("Writing shader data to: %s\n", outputPath); - return SampleWriteDataToFile(outputPath, compilationResult.Data); + return SampleWriteDataToFile(outputPath, compilationResult.Data, false); } diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index c9ed9266..58e101d2 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -16,7 +16,6 @@ typedef struct uint32_t FaceCount; } ObjMeshElementCount; -// TODO: Fix warning typedef struct { union @@ -32,6 +31,19 @@ typedef struct } Elements[3]; } ObjMeshFace; +typedef struct +{ + SampleVector3 Position; + SampleVector3 Normal; + SampleVector2 TextureCoordinates; +} InputMeshVertex; + +typedef struct +{ + InputMeshVertex* VertexList; + uint32_t VertexCount; +} InputMeshData; + ObjMeshElementCount CountObjMeshElements(ElemToolsDataSpan data) { ElemToolsDataSpan line = SampleReadLine(&data); @@ -135,6 +147,7 @@ ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) { if (faceParts[j].Length > 0) { + // Note: We are not handling negative indices result.Elements[i].Indices[j] = atoi((const char*)faceParts[j].Items); } } @@ -143,7 +156,7 @@ ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) return result; } -void ReadObjMesh(ElemToolsDataSpan data) +InputMeshData ReadObjMesh(ElemToolsDataSpan data) { ObjMeshElementCount meshElementCount = CountObjMeshElements(data); ElemToolsDataSpan line = SampleReadLine(&data); @@ -157,8 +170,8 @@ void ReadObjMesh(ElemToolsDataSpan data) SampleVector2* textCoordList = malloc(meshElementCount.TextCoordCount * sizeof(SampleVector2)); uint32_t textCoordCount = 1; - ObjMeshFace* faceList = malloc(meshElementCount.FaceCount * sizeof(ObjMeshFace)); - uint32_t faceCount = 0; + InputMeshVertex* inputMeshVertexList = malloc(meshElementCount.FaceCount * 3 * sizeof(InputMeshVertex)); + uint32_t inputMeshVertexCount = 0; while (line.Length > 0) { @@ -186,25 +199,30 @@ void ReadObjMesh(ElemToolsDataSpan data) } else if (SampleCompareString(lineParts[0], "f")) { - faceList[faceCount++] = ReadObjFace(lineParts, linePartCount); + ObjMeshFace face = ReadObjFace(lineParts, linePartCount); + + for (uint32_t i = 0; i < 3; i++) + { + InputMeshVertex vertex = + { + .Position = vertexList[face.Elements[i].VertexIndex], + .Normal = normalList[face.Elements[i].NormalIndex], + .TextureCoordinates = textCoordList[face.Elements[i].TextCoordIndex] + }; + + inputMeshVertexList[inputMeshVertexCount++] = vertex; + } } } line = SampleReadLine(&data); } - for (uint32_t i = 0; i < 10; i++) - { - printf("Vertex %d: %f, %f, %f\n", i, vertexList[i].X, vertexList[i].Y, vertexList[i].Z); - } - - for (uint32_t i = 0; i < 10; i++) + return (InputMeshData) { - for (uint32_t j = 0; j < 3; j++) - { - printf("Face %d-%d: %d, %d, %d\n", i, j, faceList[i].Elements[j].VertexIndex, faceList[i].Elements[j].NormalIndex, faceList[i].Elements[j].TextCoordIndex); - } - } + .VertexList = inputMeshVertexList, + .VertexCount = inputMeshVertexCount + }; } int main(int argc, const char* argv[]) @@ -267,5 +285,39 @@ int main(int argc, const char* argv[]) return 1; } - ReadObjMesh(inputData); + InputMeshData inputMeshData = ReadObjMesh(inputData); + printf("Input mesh vertex Count: %d\n", inputMeshData.VertexCount); + + ElemVertexBuffer vertexBuffer = + { + .Data = { .Items = (uint8_t*)inputMeshData.VertexList, .Length = inputMeshData.VertexCount * sizeof(InputMeshVertex) }, + .VertexSize = sizeof(InputMeshVertex) + }; + + ElemBuildMeshletResult result = ElemBuildMeshlets(vertexBuffer, NULL); + + // TODO: Refactor this into an util function to display messages with proper colors + for (uint32_t i = 0; i < result.Messages.Length; i++) + { + printf("Compil msg (%d): %s\n", result.Messages.Items[i].Type, result.Messages.Items[i].Message); + } + + if (result.HasErrors) + { + printf("Error while compiling shader!\n"); + return 1; + } + + // TODO: This is a good unit test + // TODO: Test cone and bounding box and spheres + for (uint32_t i = 0; i < 10; i++) + { + InputMeshVertex vertex = ((InputMeshVertex*)result.VertexBuffer.Data.Items)[i]; + + printf("Vertex X=%f, Y=%f, Z=%f\n", vertex.Position.X, vertex.Position.Y, vertex.Position.Z); + } + + printf("Writing mesh data to: %s\n", outputPath); + + SampleWriteDataToFile(outputPath, result.VertexBuffer.Data, false); } diff --git a/src/ElementalTools/Apple/PreCompiledHeader.h b/src/ElementalTools/Apple/PreCompiledHeader.h index 43e32427..0f1cfb22 100644 --- a/src/ElementalTools/Apple/PreCompiledHeader.h +++ b/src/ElementalTools/Apple/PreCompiledHeader.h @@ -33,6 +33,7 @@ #include "dxcapi.h" #include "d3d12shader.h" +#include "meshoptimizer.h" #include "metal_irconverter/metal_irconverter.h" diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp new file mode 100644 index 00000000..dd3637ff --- /dev/null +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -0,0 +1,95 @@ +#include "ElementalTools.h" +#include "SystemMemory.h" + +ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options) +{ + // TODO: Error messages + + auto stackMemoryArena = SystemGetStackMemoryArena(); + printf("Meshlet Builder\n"); + + auto indexCount = vertexBuffer.Data.Length / vertexBuffer.VertexSize; + auto vertexRemap = SystemPushArray(stackMemoryArena, indexCount); + + auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, nullptr, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); + printf("VertexCount after remap: %lu\n", vertexCount); + + auto vertexList = SystemPushArray(stackMemoryArena, vertexCount * vertexBuffer.VertexSize); + auto indexList = SystemPushArray(stackMemoryArena, indexCount); + + meshopt_remapVertexBuffer(vertexList.Pointer, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize, vertexRemap.Pointer); + meshopt_remapIndexBuffer(indexList.Pointer, nullptr, indexCount, vertexRemap.Pointer); + + meshopt_optimizeVertexCache(indexList.Pointer, indexList.Pointer, indexCount, vertexCount); + meshopt_optimizeVertexFetch(vertexList.Pointer, indexList.Pointer, indexCount, vertexList.Pointer, vertexCount, vertexBuffer.VertexSize); + + // TODO: Compute bounding sphere and bounding box + + // TODO: Review the default values + // TODO: Allow customisation + auto meshletMaxVertexCount = 64; + auto meshletMaxTriangleCount = 64; + auto coneWeight = 0.5f; + + auto meshletCount = meshopt_buildMeshletsBound(indexCount, meshletMaxVertexCount, meshletMaxTriangleCount); + printf("MeshletCount: %d\n", meshletCount); + + auto meshOptMeshletList = SystemPushArray(stackMemoryArena, meshletCount); + auto meshletVertexIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxVertexCount); + auto meshletTriangleIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxTriangleCount); + + meshletCount = meshopt_buildMeshlets(meshOptMeshletList.Pointer, + meshletVertexIndexList.Pointer, + meshletTriangleIndexList.Pointer, + indexList.Pointer, + indexCount, + (const float*)vertexList.Pointer, + vertexCount, + vertexBuffer.VertexSize, + meshletMaxVertexCount, + meshletMaxTriangleCount, + coneWeight); + + printf("MeshletCount: %d\n", meshletCount); + + auto meshletList = SystemPushArray(stackMemoryArena, meshletCount); + + for (uint32_t i = 0; i < meshletCount; i++) + { + auto meshlet = meshOptMeshletList[i]; + + meshopt_optimizeMeshlet(&meshletVertexIndexList[meshlet.vertex_offset], + &meshletTriangleIndexList[meshlet.triangle_offset], + meshlet.triangle_count, + meshlet.vertex_count); + + auto meshletBounds = meshopt_computeMeshletBounds(&meshletVertexIndexList[meshlet.vertex_offset], + &meshletTriangleIndexList[meshlet.triangle_offset], + meshlet.triangle_count, + (const float*)vertexList.Pointer, + vertexCount, + vertexBuffer.VertexSize); + + // TODO: Cone and sphere + + meshletList[i] = + { + .VertexIndexOffset = meshlet.vertex_offset, + .VertexIndexCount = meshlet.vertex_count, + .TriangleIndexOffset = meshlet.triangle_offset, + .TriangleIndexCount = meshlet.vertex_count + }; + } + + printf("Done\n"); + + return + { + .MeshletMaxVertexCount = meshletMaxVertexCount, + .MeshletMaxTriangleCount = meshletMaxTriangleCount, + .VertexBuffer = { .Data = { .Items = vertexList.Pointer, .Length = vertexList.Length }, .VertexSize = vertexBuffer.VertexSize }, + .Meshlets = { .Items = meshletList.Pointer, .Length = meshletList.Length }, + .MeshletVertexIndexBuffer = { .Items = meshletVertexIndexList.Pointer, .Length = meshletVertexIndexList.Length }, + .MeshletTriangleIndexBuffer = { .Items = meshletTriangleIndexList.Pointer, .Length = meshletTriangleIndexList.Length } + }; +} diff --git a/src/ElementalTools/Common/UnityBuild.cpp b/src/ElementalTools/Common/UnityBuild.cpp index 4486cc3f..612d5014 100644 --- a/src/ElementalTools/Common/UnityBuild.cpp +++ b/src/ElementalTools/Common/UnityBuild.cpp @@ -6,6 +6,8 @@ #include "MetalShaderConverter.cpp" #endif +#include "MeshletBuilder.cpp" + #include "SystemFunctions.cpp" #include "SystemDictionary.cpp" #include "SystemMemory.cpp" diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 23fc5277..d2c60bd3 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -196,11 +196,45 @@ typedef struct uint32_t VertexSize; } ElemVertexBuffer; +// TODO: Allow to specify the offset of the vertex position? (For now we assume vertex position is at offset 0) typedef struct { + uint32_t Reserved; } ElemBuildMeshletsOptions; -ElemToolsAPI void ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); +typedef struct +{ + uint32_t VertexIndexOffset; + uint32_t VertexIndexCount; + uint32_t TriangleIndexOffset; + uint32_t TriangleIndexCount; +} ElemMeshlet; + +typedef struct +{ + ElemMeshlet* Items; + uint32_t Length; +} ElemMeshletSpan; + +typedef struct +{ + uint32_t* Items; + uint32_t Length; +} ElemUInt32Span; + +typedef struct +{ + uint32_t MeshletMaxVertexCount; + uint32_t MeshletMaxTriangleCount; + ElemVertexBuffer VertexBuffer; + ElemMeshletSpan Meshlets; + ElemUInt32Span MeshletVertexIndexBuffer; + ElemToolsDataSpan MeshletTriangleIndexBuffer; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemBuildMeshletResult; + +ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); #ifdef UseToolsLoader #ifndef ElementalToolsLoader diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index 01d7d73c..53dc3113 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -24,7 +24,8 @@ static int functionPointersLoadedElementalTools = 0; typedef struct ElementalToolsFunctions { bool (*ElemCanCompileShader)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform); - ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, const ElemShaderSourceData*, const ElemCompileShaderOptions*); + ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, ElemShaderSourceData const *, ElemCompileShaderOptions const *); + ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemBuildMeshletsOptions const *); } ElementalToolsFunctions; @@ -78,7 +79,8 @@ static bool LoadElementalToolsFunctionPointers(void) } listElementalToolsFunctions.ElemCanCompileShader = (bool (*)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform))GetElementalToolsFunctionPointer("ElemCanCompileShader"); - listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, const ElemShaderSourceData*, const ElemCompileShaderOptions*))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); + listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, ElemShaderSourceData const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); + listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); functionPointersLoadedElementalTools = 1; @@ -116,7 +118,7 @@ static inline bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemT return listElementalToolsFunctions.ElemCanCompileShader(shaderLanguage, graphicsApi, platform); } -static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const ElemShaderSourceData* sourceData, const ElemCompileShaderOptions* options) +static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, ElemShaderSourceData const * sourceData, ElemCompileShaderOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { @@ -146,3 +148,34 @@ static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGrap return listElementalToolsFunctions.ElemCompileShaderLibrary(graphicsApi, platform, sourceData, options); } + +static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemBuildMeshletsOptions const * options) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + + #ifdef __cplusplus + ElemBuildMeshletResult result = {}; + #else + ElemBuildMeshletResult result = (ElemBuildMeshletResult){0}; + #endif + + return result; + } + + if (!listElementalToolsFunctions.ElemBuildMeshlets) + { + assert(listElementalToolsFunctions.ElemBuildMeshlets); + + #ifdef __cplusplus + ElemBuildMeshletResult result = {}; + #else + ElemBuildMeshletResult result = (ElemBuildMeshletResult){0}; + #endif + + return result; + } + + return listElementalToolsFunctions.ElemBuildMeshlets(vertexBuffer, options); +} diff --git a/src/ElementalTools/Linux/PreCompiledHeader.h b/src/ElementalTools/Linux/PreCompiledHeader.h index 9e8af397..367f377b 100644 --- a/src/ElementalTools/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Linux/PreCompiledHeader.h @@ -35,6 +35,7 @@ #include "dxcapi.h" #include "d3d12shader.h" +#include "meshoptimizer.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Microsoft/PreCompiledHeader.h index 7629f119..654f955c 100644 --- a/src/ElementalTools/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Microsoft/PreCompiledHeader.h @@ -35,6 +35,8 @@ using namespace Microsoft::WRL; #include "metal_irconverter/metal_irconverter.h" +#include "meshoptimizer.h" + #ifdef _WASDEBUG #define _DEBUG #endif From 4c78e4c196ab22bd4b5e42da50460d57467720cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 14 Aug 2024 17:41:01 +0200 Subject: [PATCH 06/93] Mesh shader rendering is working --- samples/Common/SampleInputs.h | 82 +++++++++ samples/Common/SampleMesh.h | 19 ++ samples/Common/SampleUtils.h | 8 +- samples/Elemental/03-HelloInputs/main.c | 2 +- .../05-HelloMesh/Data/RenderMesh.hlsl | 129 +++++++++++-- samples/Elemental/05-HelloMesh/main.c | 169 +++++++++++------- samples/ElementalTools/02-MeshCompiler/main.c | 35 +++- src/ElementalTools/Common/MeshletBuilder.cpp | 56 ++++-- src/ElementalTools/ElementalTools.h | 10 +- 9 files changed, 403 insertions(+), 107 deletions(-) create mode 100644 samples/Common/SampleMesh.h diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index 60d1b82b..43831e3c 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -56,6 +56,36 @@ typedef struct float ExitApp; } SampleStandardInputActions; +typedef struct +{ + float RotateLeft; + float RotateRight; + float RotateUp; + float RotateDown; + float RotateSideLeft; + float RotateSideRight; + float ZoomIn; + float ZoomOut; + + float Touch; + float TouchReleased; + float TouchRotateLeft; + float TouchRotateRight; + float TouchRotateUp; + float TouchRotateDown; + float TouchPositionX; + float TouchPositionY; + + float Touch2; + float Touch2PositionX; + float Touch2PositionY; + + float TouchRotateSide; + float TriangleColor; + float ShowCursor; + float ExitApp; +} SampleModelViewerInputActions; + void SampleRegisterInputActionBinding(SampleInputActionBindingSpan* bindings, ElemInputId inputId, uint32_t index, SampleInputActionBindingType bindingType, float* actionValue) { bindings->Items[bindings->Length++] = (SampleInputActionBinding) @@ -177,3 +207,55 @@ void SampleRegisterStandardInputBindings(SampleInputActionBindingSpan* inputActi SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Released, &inputActions->TouchReleased); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->Action1); } + +void SampleRegisterModelViewerInputBindings(SampleInputActionBindingSpan* inputActionBindings, SampleModelViewerInputActions* inputActions) +{ + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyA, 0, SampleInputActionBindingType_Value, &inputActions->RotateLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyD, 0, SampleInputActionBindingType_Value, &inputActions->RotateRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyW, 0, SampleInputActionBindingType_Value, &inputActions->RotateUp); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyS, 0, SampleInputActionBindingType_Value, &inputActions->RotateDown); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyQ, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyE, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyZ, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyX, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_ReleasedSwitch, &inputActions->ShowCursor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); + + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Value, &inputActions->Touch); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->TriangleColor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Released, &inputActions->TouchReleased); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseRightButton, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateSide); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisXPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisYNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateUp); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateDown); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseWheelPositive, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseWheelNegative, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseMiddleButton, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); + + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputActions->RotateLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputActions->RotateRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputActions->RotateUp); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputActions->RotateDown); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickButton, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); + + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputActions->Touch); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateUp); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateDown); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputActions->TouchPositionX); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputActions->TouchPositionY); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 1, SampleInputActionBindingType_Value, &inputActions->Touch2); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputActions->Touch2PositionX); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputActions->Touch2PositionY); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Released, &inputActions->TouchReleased); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->TriangleColor); +} + diff --git a/samples/Common/SampleMesh.h b/samples/Common/SampleMesh.h new file mode 100644 index 00000000..bf3a5fee --- /dev/null +++ b/samples/Common/SampleMesh.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +typedef struct +{ + char FileId[4]; + uint32_t MeshletCount; + uint8_t MeshletMaxVertexCount; + uint8_t MeshletMaxTriangleCount; + uint32_t VertexBufferOffset; + uint32_t VertexBufferSizeInBytes; + uint32_t MeshletBufferOffset; + uint32_t MeshletBufferSizeInBytes; + uint32_t MeshletVertexIndexBufferOffset; + uint32_t MeshletVertexIndexBufferSizeInBytes; + uint32_t MeshletTriangleIndexBufferOffset; + uint32_t MeshletTriangleIndexBufferSizeInBytes; +} SampleMeshHeader; diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index 9b0a1346..609950e7 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -174,11 +174,13 @@ int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) return -1; } + const char* fileMode = append ? "ab" : "wb"; + #ifdef _WIN32 FILE* file; - fopen_s(&file, filename, "wb"); + fopen_s(&file, filename, fileMode); #else - FILE* file = fopen(filename, "wb"); + FILE* file = fopen(filename, fileMode); #endif if (file == NULL) @@ -192,7 +194,7 @@ int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) if (bytesWritten < data.Length) { - printf("ERROR 3\n"); + printf("ERROR 3: %zu\n", bytesWritten); return -1; // Return -1 if not all bytes were written } diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 3881816b..c6ca9c18 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -362,7 +362,6 @@ void UpdateGameState(GameState* gameState, InputActions* inputActions, float del void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - applicationPayload->ShaderParameters.AspectRatio = updateParameters->SwapChainInfo.AspectRatio; InputActions* inputActions = &applicationPayload->InputActions; UpdateInputActions(&applicationPayload->InputActionBindings); @@ -393,6 +392,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void applicationPayload->ShaderParameters.RotationQuaternion = SampleMulQuat(rotationQuaternion, applicationPayload->ShaderParameters.RotationQuaternion); } + applicationPayload->ShaderParameters.AspectRatio = updateParameters->SwapChainInfo.AspectRatio; float maxZoom = (applicationPayload->ShaderParameters.AspectRatio >= 0.75 ? 1.5f : 3.5f); applicationPayload->ShaderParameters.Zoom = fminf(maxZoom, gameState->Zoom); applicationPayload->ShaderParameters.TriangeColor = inputActions->TriangleColor; diff --git a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl index 850422b7..f1716760 100644 --- a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl @@ -1,46 +1,147 @@ struct ShaderParameters { uint32_t VertexBufferIndex; + uint32_t MeshletBufferIndex; + uint32_t MeshletVertexIndexBufferIndex; + uint32_t MeshletTriangleIndexBufferIndex; + float4 RotationQuaternion; + float Zoom; + float AspectRatio; + uint32_t TriangleColor; }; [[vk::push_constant]] ShaderParameters parameters : register(b0); +struct ElemMeshlet +{ + uint32_t VertexIndexOffset; + uint32_t VertexIndexCount; + uint32_t TriangleOffset; + uint32_t TriangleCount; +}; + +struct Vertex +{ + float3 Position; + float3 Normal; + float2 TextureCoordinates; +}; + struct VertexOutput { float4 Position: SV_Position; + float3 WorldNormal: NORMAL0; + uint MeshletIndex : COLOR0; }; -static uint3 quadIndices[] = +// TODO: Put that in an include file +float4x4 TransformMatrix(float4 quaternion, float3 translation) { - uint3(0, 1, 2), - uint3(2, 1, 3) -}; + float xw = quaternion.x*quaternion.w, xx = quaternion.x*quaternion.x, yy = quaternion.y*quaternion.y, + yw = quaternion.y*quaternion.w, xy = quaternion.x*quaternion.y, yz = quaternion.y*quaternion.z, + zw = quaternion.z*quaternion.w, xz = quaternion.x*quaternion.z, zz = quaternion.z*quaternion.z; + + float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); + float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); + float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); + float4 row4 = float4(0.0, 0.0, 0.0, 1.0); + + return float4x4(row1, row2, row3, row4); +} + +float4x4 LookAtLHMatrix(float3 eyePosition, float3 targetPosition, float3 upDirection) +{ + float3 forwardDirection = normalize(targetPosition - eyePosition); + float3 rightDirection = normalize(cross(upDirection, forwardDirection)); + float3 upDirectionNew = cross(forwardDirection, rightDirection); + + float4 row1 = float4(rightDirection.x, upDirectionNew.x, forwardDirection.x, 0.0); + float4 row2 = float4(rightDirection.y, upDirectionNew.y, forwardDirection.y, 0.0); + float4 row3 = float4(rightDirection.z, upDirectionNew.z, forwardDirection.z, 0.0); + float4 row4 = float4(-dot(rightDirection, eyePosition), -dot(upDirectionNew, eyePosition), -dot(forwardDirection, eyePosition), 1.0); + + return float4x4(row1, row2, row3, row4); +} + +float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) +{ + float height = 1.0 / tan(fovY * 0.5); + + float4 row1 = float4(height / aspectRatio, 0.0f, 0.0f, 0.0f); + float4 row2 = float4(0.0f, height, 0.0f, 0.0f); + float4 row3 = float4(0.0f, 0.0f, 0, 1.0f); + float4 row4 = float4(0.0f, 0.0f, zNear, 0.0f); + + return float4x4(row1, row2, row3, row4); +} [shader("mesh")] [OutputTopology("triangle")] -[NumThreads(32, 1, 1)] -void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) +[NumThreads(126, 1, 1)] +void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[64], out indices uint3 indices[126]) { - const uint meshVertexCount = 3; - const uint triangleCount = 1; + ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; + ElemMeshlet meshlet = meshletBuffer.Load(groupId * sizeof(ElemMeshlet)); - SetMeshOutputCounts(meshVertexCount, triangleCount); + SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); - if (groupThreadId < meshVertexCount) + if (groupThreadId < meshlet.VertexIndexCount) { + // TODO: Compute matrix in task shader + float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; + + float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); + float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); + float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); + + float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); + + ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - vertices[groupThreadId].Position = float4(vertexBuffer.Load(groupThreadId * sizeof(float3)), 1.0); + + uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); + Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); + + vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); + vertices[groupThreadId].WorldNormal = mul(vertex.Normal, worldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].MeshletIndex = groupId; } - if (groupThreadId < triangleCount) + if (groupThreadId < meshlet.TriangleCount) { - indices[groupThreadId] = quadIndices[groupThreadId]; + ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; + uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); + + uint32_t4 unpacked = unpack_u8u32(triangleIndex); + indices[groupThreadId] = unpacked.xyz; } } +uint hash(uint a) +{ + a = (a+0x7ed55d16) + (a<<12); + a = (a^0xc761c23c) ^ (a>>19); + a = (a+0x165667b1) + (a<<5); + a = (a+0xd3a2646c) ^ (a<<9); + a = (a+0xfd7046c5) + (a<<3); + a = (a^0xb55a4f09) ^ (a>>16); + + return a; +} + [shader("pixel")] float4 PixelMain(const VertexOutput input) : SV_Target0 { - return float4(1.0, 1.0, 0.0, 1.0); + if (parameters.TriangleColor == 0) + { + return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1); + } + else + { + uint hashResult = hash(input.MeshletIndex); + float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; + + return float4(meshletColor, 1); + } } diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index 7fc0b977..de771315 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -3,28 +3,34 @@ #include "SampleUtils.h" #include "SampleMath.h" #include "SampleInputs.h" +#include "SampleMesh.h" -#define ROTATION_TOUCH_SPEED 0.5f +#define ROTATION_TOUCH_DECREASE_SPEED 0.001f +#define ROTATION_TOUCH_SPEED 4.0f #define ROTATION_TOUCH_MAX_DELTA 0.3f -#define ROTATION_MULTITOUCH_SPEED 100.0f -#define ROTATION_ACCELERATION 50.0f -#define ROTATION_FRICTION 40.0f -#define ZOOM_MULTITOUCH_SPEED 100.0f -#define ZOOM_SPEED 0.5f -#define ZOOM_FACTOR 10.0f +#define ROTATION_MULTITOUCH_SPEED 200.0f +#define ROTATION_ACCELERATION 500.0f +#define ROTATION_FRICTION 60.0f +#define ZOOM_MULTITOUCH_SPEED 1000.0f +#define ZOOM_SPEED 5.0f typedef struct { uint32_t VertexBuffer; - SampleMatrix3x3 Transform; + uint32_t MeshletBuffer; + uint32_t MeshletVertexIndexBuffer; + uint32_t MeshletTriangleIndexBuffer; + SampleVector4 RotationQuaternion; + float Zoom; + float AspectRatio; + uint32_t TriangeColor; } ShaderParameters; typedef struct { - SampleVector2 TranslationDelta; - float RotationDelta; + SampleVector3 RotationDelta; SampleVector2 RotationTouch; - SampleVector2 CurrentTranslationSpeed; + SampleVector3 CurrentRotationSpeed; float PreviousTouchDistance; float PreviousTouchAngle; float Zoom; @@ -32,8 +38,15 @@ typedef struct typedef struct { + uint32_t MeshletCount; ElemGraphicsResource VertexBuffer; ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; + ElemGraphicsResource MeshletBuffer; + ElemGraphicsResourceDescriptor MeshletBufferReadDescriptor; + ElemGraphicsResource MeshletVertexIndexBuffer; + ElemGraphicsResourceDescriptor MeshletVertexIndexBufferReadDescriptor; + ElemGraphicsResource MeshletTriangleIndexBuffer; + ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; } MeshData; // TODO: Group common variables into separate structs @@ -49,7 +62,7 @@ typedef struct ElemSwapChain SwapChain; ElemPipelineState GraphicsPipeline; ShaderParameters ShaderParameters; - SampleStandardInputActions InputActions; + SampleModelViewerInputActions InputActions; SampleInputActionBindingSpan InputActionBindings; GameState GameState; MeshData TestMeshData; @@ -57,26 +70,42 @@ typedef struct void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); -void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) +// TODO: To remove when IOQueues +void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, void* dataPointer, uint32_t sizeInBytes) { // TODO: Alignment should be used with the offset before adding the size of the resource! - ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, 10 * sizeof(SampleVector3), ElemGraphicsResourceUsage_Read, NULL); + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); - meshData->VertexBuffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); + *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; - float vertices[] = + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(*buffer); + memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); + + *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); +} + +void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) +{ + // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! + // Add a parameter to specify the length we want to read and the offset + ElemDataSpan meshFileData = SampleReadFile(path); + + uint8_t* fileDataPointer = meshFileData.Items; + SampleMeshHeader* meshHeader = (SampleMeshHeader*)fileDataPointer; + + if (meshHeader->FileId[0] == 'M' && meshHeader->FileId[1] == 'E' && meshHeader->FileId[2] == 'S' && meshHeader->FileId[3] == 'H') { - -1.0f, 1.0f, 0.0f, - 0.0f, 1.0f, 0.0f, - -1.0f, 0.0f, 0.0f - }; + printf("OK Meshlet Count: %d\n", meshHeader->MeshletCount); + } - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->TestMeshData.VertexBuffer); - memcpy(vertexBufferPointer.Items, vertices, sizeof(vertices)); + meshData->MeshletCount = meshHeader->MeshletCount; - meshData->VertexBufferReadDescriptor = ElemCreateGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBuffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + CreateAndUploadDataTemp(&meshData->VertexBuffer, &meshData->VertexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->VertexBufferOffset, meshHeader->VertexBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletBuffer, &meshData->MeshletBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletBufferOffset, meshHeader->MeshletBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletVertexIndexBuffer, &meshData->MeshletVertexIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletVertexIndexBufferOffset, meshHeader->MeshletVertexIndexBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletTriangleIndexBuffer, &meshData->MeshletTriangleIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletTriangleIndexBufferOffset, meshHeader->MeshletTriangleIndexBufferSizeInBytes); } void InitSample(void* payload) @@ -94,8 +123,11 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); - LoadMesh(&applicationPayload->TestMeshData, "Pouet.mesh", applicationPayload); + LoadMesh(&applicationPayload->TestMeshData, "kitten.mesh", applicationPayload); applicationPayload->ShaderParameters.VertexBuffer = applicationPayload->TestMeshData.VertexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletBuffer = applicationPayload->TestMeshData.MeshletBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = applicationPayload->TestMeshData.MeshletVertexIndexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = applicationPayload->TestMeshData.MeshletTriangleIndexBufferReadDescriptor; ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -110,12 +142,10 @@ void InitSample(void* payload) ElemFreeShaderLibrary(shaderLibrary); - applicationPayload->ShaderParameters.Transform = SampleCreateIdentityMatrix(); + applicationPayload->ShaderParameters.RotationQuaternion = (SampleVector4){ .X = 0, .Y = 0, .Z = 0, .W = 1 }; applicationPayload->InputActions.ShowCursor = true; - applicationPayload->GameState.Zoom = 1.0f; - applicationPayload->GameState.RotationDelta = 0.0f; - SampleRegisterStandardInputBindings(&applicationPayload->InputActionBindings, &applicationPayload->InputActions); + SampleRegisterModelViewerInputBindings(&applicationPayload->InputActionBindings, &applicationPayload->InputActions); SampleStartFrameMeasurement(); } @@ -133,6 +163,12 @@ void FreeSample(void* payload) // Free Mesh ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBufferReadDescriptor, NULL); ElemFreeGraphicsResource(applicationPayload->TestMeshData.VertexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletVertexIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletVertexIndexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletTriangleIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletTriangleIndexBuffer, NULL); ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); @@ -145,12 +181,9 @@ void ResetTouchParameters(GameState* gameState) gameState->PreviousTouchAngle = 0.0f; } -void UpdateGameState(GameState* gameState, SampleStandardInputActions* inputActions, float deltaTimeInSeconds) +void UpdateGameState(GameState* gameState, SampleModelViewerInputActions* inputActions, float deltaTimeInSeconds) { - gameState->TranslationDelta = V2Zero; - - float zoomDelta = 0.0f; - float rotationDeltaZ = 0.0f; + gameState->RotationDelta = V3Zero; if (inputActions->Touch) { @@ -165,12 +198,12 @@ void UpdateGameState(GameState* gameState, SampleStandardInputActions* inputActi if (gameState->PreviousTouchDistance != 0.0f) { - zoomDelta = -(distance - gameState->PreviousTouchDistance) * ZOOM_MULTITOUCH_SPEED * deltaTimeInSeconds; + gameState->Zoom += (distance - gameState->PreviousTouchDistance) * ZOOM_MULTITOUCH_SPEED * deltaTimeInSeconds; } if (gameState->PreviousTouchAngle != 0.0f) { - rotationDeltaZ = -SampleNormalizeAngle(angle - gameState->PreviousTouchAngle) * ROTATION_MULTITOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationDelta.Z = -SampleNormalizeAngle(angle - gameState->PreviousTouchAngle) * ROTATION_MULTITOUCH_SPEED * deltaTimeInSeconds; } gameState->PreviousTouchDistance = distance; @@ -180,65 +213,67 @@ void UpdateGameState(GameState* gameState, SampleStandardInputActions* inputActi { ResetTouchParameters(gameState); - gameState->TranslationDelta.X = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - gameState->TranslationDelta.Y = (inputActions->TouchTranslateUp - inputActions->TouchTranslateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationDelta.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationDelta.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } } else if (inputActions->TouchRotateSide) { ResetTouchParameters(gameState); - rotationDeltaZ = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationDelta.Z = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } else if (inputActions->TouchReleased && !inputActions->Touch2) { ResetTouchParameters(gameState); - gameState->RotationTouch.X = (inputActions->TouchTranslateUp - inputActions->TouchTranslateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - gameState->RotationTouch.Y = (inputActions->TouchTranslateLeft - inputActions->TouchTranslateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + gameState->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } else { - SampleVector2 direction = SampleNormalizeV2((SampleVector2) + SampleVector3 direction = SampleNormalizeV3((SampleVector3) { - .X = (inputActions->TranslateRight - inputActions->TranslateLeft), - .Y = (inputActions->TranslateDown - inputActions->TranslateUp), + .X = (inputActions->RotateUp - inputActions->RotateDown), + .Y = (inputActions->RotateLeft - inputActions->RotateRight), + .Z = (inputActions->RotateSideLeft - inputActions->RotateSideRight) }); - if (SampleMagnitudeSquaredV2(direction)) + if (SampleMagnitudeSquaredV3(direction)) { - SampleVector2 acceleration = SampleAddV2(SampleMulScalarV2(direction, ROTATION_ACCELERATION), SampleMulScalarV2(SampleInverseV2(gameState->CurrentTranslationSpeed), ROTATION_FRICTION)); + SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(gameState->CurrentRotationSpeed), ROTATION_FRICTION)); ResetTouchParameters(gameState); - - gameState->TranslationDelta = SampleAddV2(SampleMulScalarV2(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV2(gameState->CurrentTranslationSpeed, deltaTimeInSeconds)); - gameState->CurrentTranslationSpeed = SampleAddV2(SampleMulScalarV2(acceleration, deltaTimeInSeconds), gameState->CurrentTranslationSpeed); + gameState->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(gameState->CurrentRotationSpeed, deltaTimeInSeconds)); + gameState->CurrentRotationSpeed = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), gameState->CurrentRotationSpeed); } - - rotationDeltaZ = (inputActions->RotateSideLeft - inputActions->RotateSideRight) * 1 * deltaTimeInSeconds; - zoomDelta = -(inputActions->ZoomIn - inputActions->ZoomOut) * ZOOM_SPEED * deltaTimeInSeconds; } - if (zoomDelta != 0) + if (SampleMagnitudeSquaredV2(gameState->RotationTouch) > 0) { - if (zoomDelta > 0) + if (SampleMagnitudeV2(gameState->RotationTouch) > ROTATION_TOUCH_MAX_DELTA) { - gameState->Zoom *= powf(ZOOM_FACTOR, zoomDelta); + gameState->RotationTouch = SampleMulScalarV2(SampleNormalizeV2(gameState->RotationTouch), ROTATION_TOUCH_MAX_DELTA); } - else + + gameState->RotationDelta = SampleAddV3(gameState->RotationDelta, (SampleVector3){ gameState->RotationTouch.X, gameState->RotationTouch.Y, 0.0f }); + + SampleVector2 inverse = SampleMulScalarV2(SampleNormalizeV2(SampleInverseV2(gameState->RotationTouch)), ROTATION_TOUCH_DECREASE_SPEED); + gameState->RotationTouch = SampleAddV2(gameState->RotationTouch, inverse); + + if (SampleMagnitudeV2(gameState->RotationTouch) < 0.001f) { - gameState->Zoom /= powf(ZOOM_FACTOR, -zoomDelta); + ResetTouchParameters(gameState); } } - gameState->TranslationDelta = SampleMulScalarV2(gameState->TranslationDelta, gameState->Zoom); - gameState->RotationDelta = rotationDeltaZ; + gameState->Zoom += (inputActions->ZoomIn - inputActions->ZoomOut) * ZOOM_SPEED * deltaTimeInSeconds; } void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - SampleStandardInputActions* inputActions = &applicationPayload->InputActions; + SampleModelViewerInputActions* inputActions = &applicationPayload->InputActions; SampleUpdateInputActions(&applicationPayload->InputActionBindings); if (inputActions->ExitApp) @@ -258,9 +293,19 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void GameState* gameState = &applicationPayload->GameState; UpdateGameState(gameState, inputActions, updateParameters->DeltaTimeInSeconds); - SampleMatrix3x3 transformMatrix = SampleMulMatrix3x3(SampleCreateTranslationMatrix(gameState->TranslationDelta.X, gameState->TranslationDelta.Y), SampleCreateRotationMatrix(gameState->RotationDelta)); + if (SampleMagnitudeSquaredV3(gameState->RotationDelta)) + { + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, gameState->RotationDelta.X), + SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, gameState->RotationDelta.Z), + SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, gameState->RotationDelta.Y))); + + applicationPayload->ShaderParameters.RotationQuaternion = SampleMulQuat(rotationQuaternion, applicationPayload->ShaderParameters.RotationQuaternion); + } - applicationPayload->ShaderParameters.Transform = SampleMulMatrix3x3(applicationPayload->ShaderParameters.Transform, transformMatrix); + applicationPayload->ShaderParameters.AspectRatio = updateParameters->SwapChainInfo.AspectRatio; + float maxZoom = (applicationPayload->ShaderParameters.AspectRatio >= 0.75 ? 1.5f : 3.5f); + applicationPayload->ShaderParameters.Zoom = fminf(maxZoom, gameState->Zoom); + applicationPayload->ShaderParameters.TriangeColor = inputActions->TriangleColor; ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); @@ -279,7 +324,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - ElemDispatchMesh(commandList, 1, 1, 1); + ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); ElemEndRenderPass(commandList); diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index 58e101d2..8213cbac 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -1,6 +1,7 @@ #include "ElementalTools.h" #include "SampleUtils.h" #include "SampleMath.h" +#include "SampleMesh.h" /** * WARNING: This obj parsing code is just for demonstration purpose only. The parsing is not feature complete @@ -23,8 +24,8 @@ typedef struct struct { uint32_t VertexIndex; - uint32_t NormalIndex; uint32_t TextCoordIndex; + uint32_t NormalIndex; }; uint32_t Indices[3]; @@ -131,10 +132,10 @@ ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) if (linePartCount < 4) { printf("Error: Invalid number of elements in the line for Face.\n"); - return (ObjMeshFace) { 0 }; + return (ObjMeshFace) {}; } - ObjMeshFace result = { 0 }; + ObjMeshFace result = {}; for (uint32_t i = 0; i < 3; i++) { @@ -162,12 +163,15 @@ InputMeshData ReadObjMesh(ElemToolsDataSpan data) ElemToolsDataSpan line = SampleReadLine(&data); SampleVector3* vertexList = malloc(meshElementCount.VertexCount * sizeof(SampleVector3)); + memset(vertexList, 0, meshElementCount.VertexCount * sizeof(SampleVector3)); uint32_t vertexCount = 1; SampleVector3* normalList = malloc(meshElementCount.NormalCount * sizeof(SampleVector3)); + memset(normalList, 0, meshElementCount.NormalCount * sizeof(SampleVector3)); uint32_t normalCount = 1; SampleVector2* textCoordList = malloc(meshElementCount.TextCoordCount * sizeof(SampleVector2)); + memset(textCoordList, 0, meshElementCount.TextCoordCount * sizeof(SampleVector2)); uint32_t textCoordCount = 1; InputMeshVertex* inputMeshVertexList = malloc(meshElementCount.FaceCount * 3 * sizeof(InputMeshVertex)); @@ -318,6 +322,29 @@ int main(int argc, const char* argv[]) } printf("Writing mesh data to: %s\n", outputPath); + + uint32_t meshletBufferSize = result.Meshlets.Length * sizeof(ElemMeshlet); + uint32_t meshletVertexIndexBufferSize = result.MeshletVertexIndexBuffer.Length * sizeof(uint32_t); + + SampleMeshHeader meshHeader = + { + .FileId = { 'M', 'E', 'S', 'H' }, + .MeshletCount = result.Meshlets.Length, + .MeshletMaxVertexCount = result.MeshletMaxVertexCount, + .MeshletMaxTriangleCount = result.MeshletMaxTriangleCount, + .VertexBufferOffset = sizeof(SampleMeshHeader), + .VertexBufferSizeInBytes = result.VertexBuffer.Data.Length, + .MeshletBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length, + .MeshletBufferSizeInBytes = meshletBufferSize, + .MeshletVertexIndexBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length + meshletBufferSize, + .MeshletVertexIndexBufferSizeInBytes = meshletVertexIndexBufferSize, + .MeshletTriangleIndexBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length + meshletBufferSize + meshletVertexIndexBufferSize, + .MeshletTriangleIndexBufferSizeInBytes = result.MeshletTriangleIndexBuffer.Length * sizeof(uint32_t) + }; - SampleWriteDataToFile(outputPath, result.VertexBuffer.Data, false); + SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)&meshHeader, .Length = sizeof(SampleMeshHeader) }, false); + SampleWriteDataToFile(outputPath, result.VertexBuffer.Data, true); + SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.Meshlets.Items, .Length = meshletBufferSize}, true); + SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.MeshletVertexIndexBuffer.Items, .Length = result.MeshletVertexIndexBuffer.Length * sizeof(uint32_t) }, true); + SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.MeshletTriangleIndexBuffer.Items, .Length = result.MeshletTriangleIndexBuffer.Length * sizeof(uint32_t) }, true); } diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp index dd3637ff..8e1cce85 100644 --- a/src/ElementalTools/Common/MeshletBuilder.cpp +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -9,13 +9,13 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf printf("Meshlet Builder\n"); auto indexCount = vertexBuffer.Data.Length / vertexBuffer.VertexSize; - auto vertexRemap = SystemPushArray(stackMemoryArena, indexCount); + auto vertexRemap = SystemPushArrayZero(stackMemoryArena, indexCount); auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, nullptr, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); - printf("VertexCount after remap: %lu\n", vertexCount); + printf("VertexCount after remap: %zu\n", vertexCount); - auto vertexList = SystemPushArray(stackMemoryArena, vertexCount * vertexBuffer.VertexSize); - auto indexList = SystemPushArray(stackMemoryArena, indexCount); + auto vertexList = SystemPushArrayZero(stackMemoryArena, vertexCount * vertexBuffer.VertexSize); + auto indexList = SystemPushArrayZero(stackMemoryArena, indexCount); meshopt_remapVertexBuffer(vertexList.Pointer, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize, vertexRemap.Pointer); meshopt_remapIndexBuffer(indexList.Pointer, nullptr, indexCount, vertexRemap.Pointer); @@ -27,20 +27,21 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf // TODO: Review the default values // TODO: Allow customisation - auto meshletMaxVertexCount = 64; - auto meshletMaxTriangleCount = 64; + uint8_t meshletMaxVertexCount = 64u; + uint8_t meshletMaxTriangleCount = 64u; auto coneWeight = 0.5f; - auto meshletCount = meshopt_buildMeshletsBound(indexCount, meshletMaxVertexCount, meshletMaxTriangleCount); + auto meshletCount = (uint32_t)meshopt_buildMeshletsBound(indexCount, meshletMaxVertexCount, meshletMaxTriangleCount); printf("MeshletCount: %d\n", meshletCount); auto meshOptMeshletList = SystemPushArray(stackMemoryArena, meshletCount); auto meshletVertexIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxVertexCount); - auto meshletTriangleIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxTriangleCount); + auto meshletTriangleIndexListRaw = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxTriangleCount * 3); + auto meshletTriangleIndexList = SystemPushArrayZero(stackMemoryArena, meshletCount * meshletMaxTriangleCount); meshletCount = meshopt_buildMeshlets(meshOptMeshletList.Pointer, meshletVertexIndexList.Pointer, - meshletTriangleIndexList.Pointer, + meshletTriangleIndexListRaw.Pointer, indexList.Pointer, indexCount, (const float*)vertexList.Pointer, @@ -50,6 +51,10 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf meshletMaxTriangleCount, coneWeight); + + auto meshletVertexIndexCount = 0u; + auto meshletTriangleIndexCount = 0u; + printf("MeshletCount: %d\n", meshletCount); auto meshletList = SystemPushArray(stackMemoryArena, meshletCount); @@ -59,12 +64,12 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto meshlet = meshOptMeshletList[i]; meshopt_optimizeMeshlet(&meshletVertexIndexList[meshlet.vertex_offset], - &meshletTriangleIndexList[meshlet.triangle_offset], + &meshletTriangleIndexListRaw[meshlet.triangle_offset], meshlet.triangle_count, meshlet.vertex_count); auto meshletBounds = meshopt_computeMeshletBounds(&meshletVertexIndexList[meshlet.vertex_offset], - &meshletTriangleIndexList[meshlet.triangle_offset], + &meshletTriangleIndexListRaw[meshlet.triangle_offset], meshlet.triangle_count, (const float*)vertexList.Pointer, vertexCount, @@ -76,20 +81,35 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf { .VertexIndexOffset = meshlet.vertex_offset, .VertexIndexCount = meshlet.vertex_count, - .TriangleIndexOffset = meshlet.triangle_offset, - .TriangleIndexCount = meshlet.vertex_count + .TriangleOffset = meshlet.triangle_offset / 3, + .TriangleCount = meshlet.triangle_count }; + + for (uint32_t j = 0; j < meshlet.triangle_count; j++) + { + printf("Processing Triangle: %d (meshlet %d)\n", j, i); + auto pointer = &meshletTriangleIndexListRaw[meshlet.triangle_offset + j * 3]; + + auto p0 = pointer[0]; + auto p1 = pointer[1]; + auto p2 = pointer[2]; + + meshletTriangleIndexList[meshletList[i].TriangleOffset + j] = ((uint32_t)p2) << 16 | ((uint32_t)p1) << 8 | (uint32_t)p0; + } + + meshletVertexIndexCount += meshlet.vertex_count; + meshletTriangleIndexCount += meshlet.triangle_count; } printf("Done\n"); - + return { .MeshletMaxVertexCount = meshletMaxVertexCount, .MeshletMaxTriangleCount = meshletMaxTriangleCount, - .VertexBuffer = { .Data = { .Items = vertexList.Pointer, .Length = vertexList.Length }, .VertexSize = vertexBuffer.VertexSize }, - .Meshlets = { .Items = meshletList.Pointer, .Length = meshletList.Length }, - .MeshletVertexIndexBuffer = { .Items = meshletVertexIndexList.Pointer, .Length = meshletVertexIndexList.Length }, - .MeshletTriangleIndexBuffer = { .Items = meshletTriangleIndexList.Pointer, .Length = meshletTriangleIndexList.Length } + .VertexBuffer = { .Data = { .Items = vertexList.Pointer, .Length = (uint32_t)vertexList.Length }, .VertexSize = vertexBuffer.VertexSize }, + .Meshlets = { .Items = meshletList.Pointer, .Length = (uint32_t)meshletList.Length }, + .MeshletVertexIndexBuffer = { .Items = meshletVertexIndexList.Pointer, .Length = meshletVertexIndexCount }, + .MeshletTriangleIndexBuffer = { .Items = meshletTriangleIndexList.Pointer, .Length = meshletTriangleIndexCount } }; } diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index d2c60bd3..659a3c63 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -206,8 +206,8 @@ typedef struct { uint32_t VertexIndexOffset; uint32_t VertexIndexCount; - uint32_t TriangleIndexOffset; - uint32_t TriangleIndexCount; + uint32_t TriangleOffset; + uint32_t TriangleCount; } ElemMeshlet; typedef struct @@ -224,12 +224,12 @@ typedef struct typedef struct { - uint32_t MeshletMaxVertexCount; - uint32_t MeshletMaxTriangleCount; + uint8_t MeshletMaxVertexCount; + uint8_t MeshletMaxTriangleCount; ElemVertexBuffer VertexBuffer; ElemMeshletSpan Meshlets; ElemUInt32Span MeshletVertexIndexBuffer; - ElemToolsDataSpan MeshletTriangleIndexBuffer; + ElemUInt32Span MeshletTriangleIndexBuffer; ElemToolsMessageSpan Messages; bool HasErrors; } ElemBuildMeshletResult; From db2f63eb30ea1265e6e1dd6355ed6a50eb51f9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 17 Aug 2024 11:27:20 +0200 Subject: [PATCH 07/93] Mesh rendering WIP --- samples/Common/SampleUtils.h | 1 - samples/Elemental/02-HelloTriangle/main.c | 2 +- samples/Elemental/03-HelloInputs/main.c | 2 +- samples/Elemental/04-HelloCompute/main.c | 2 +- samples/Elemental/05-HelloMesh/main.c | 50 +++++- samples/ElementalTools/02-MeshCompiler/main.c | 83 ++++++--- samples/XX-MinimalTriangle/main.c | 2 +- .../Common/Graphics/ResourceBarrier.h | 1 + .../Common/Graphics/Vulkan/VulkanShader.cpp | 2 +- src/Elemental/Elemental.h | 22 ++- .../Graphics/DirectX12GraphicsDevice.cpp | 3 + .../Graphics/DirectX12GraphicsDevice.h | 1 + .../Microsoft/Graphics/DirectX12Rendering.cpp | 113 ++++++++---- .../Microsoft/Graphics/DirectX12Resource.cpp | 63 ++++++- .../Microsoft/Graphics/DirectX12Resource.h | 2 + .../Graphics/DirectX12ResourceBarrier.cpp | 28 +-- .../Microsoft/Graphics/DirectX12Shader.cpp | 27 +-- .../Common/DirectXShaderCompiler.cpp | 1 + src/ElementalTools/Common/MeshletBuilder.cpp | 6 +- tests/GraphicsTests/GraphicsTests.cpp | 43 ++--- tests/GraphicsTests/GraphicsTests.h | 4 +- tests/GraphicsTests/RenderingTests.cpp | 95 +++++++++- tests/GraphicsTests/ResourceBarrierTests.cpp | 28 ++- tests/GraphicsTests/ResourceTests.cpp | 168 ++++++++++++++++++ .../GraphicsTests/Shaders/RenderingTests.hlsl | 10 ++ tests/ToolsTests/MeshBuilderTests.cpp | 135 ++++++++++++++ tests/ToolsTests/UnityBuild.cpp | 1 + 27 files changed, 759 insertions(+), 136 deletions(-) create mode 100644 tests/ToolsTests/MeshBuilderTests.cpp diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index 609950e7..c4a54555 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -167,7 +167,6 @@ ElemDataSpan SampleReadFile(const char* filename) int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) { - printf("Length:%s %d\n", filename, data.Length); if (filename == NULL || data.Length == 0) { printf("ERROR 1\n"); diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c index a12aefe6..225f3c3a 100644 --- a/samples/Elemental/02-HelloTriangle/main.c +++ b/samples/Elemental/02-HelloTriangle/main.c @@ -41,7 +41,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index c6ca9c18..55c3230b 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -182,7 +182,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } }); applicationPayload->ShaderParameters.RotationQuaternion = (SampleVector4){ .X = 0, .Y = 0, .Z = 0, .W = 1 }; diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index 8b45f367..dd7706f3 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -118,7 +118,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index de771315..9bc3ece1 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -26,6 +26,7 @@ typedef struct uint32_t TriangeColor; } ShaderParameters; +// TODO: Extract from the gamestate the inputState typedef struct { SampleVector3 RotationDelta; @@ -60,6 +61,8 @@ typedef struct uint32_t CurrentHeapOffset; ElemFence LastExecutionFence; ElemSwapChain SwapChain; + ElemGraphicsHeap DepthBufferHeap; + ElemGraphicsResource DepthBuffer; ElemPipelineState GraphicsPipeline; ShaderParameters ShaderParameters; SampleModelViewerInputActions InputActions; @@ -70,6 +73,23 @@ typedef struct void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); +void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, uint32_t height) +{ + if (applicationPayload->DepthBuffer != ELEM_HANDLE_NULL) + { + ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); + } + + printf("Creating DepthBuffer...\n"); + + ElemGraphicsResourceInfo resourceInfo = ElemCreateTexture2DResourceInfo(applicationPayload->GraphicsDevice, width, height, 1, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil, + &(ElemGraphicsResourceInfoOptions) { + .DebugName = "DepthBuffer" + }); + + applicationPayload->DepthBuffer = ElemCreateGraphicsResource(applicationPayload->DepthBufferHeap, 0, &resourceInfo); +} + // TODO: To remove when IOQueues void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, void* dataPointer, uint32_t sizeInBytes) { @@ -106,6 +126,11 @@ void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicat CreateAndUploadDataTemp(&meshData->MeshletBuffer, &meshData->MeshletBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletBufferOffset, meshHeader->MeshletBufferSizeInBytes); CreateAndUploadDataTemp(&meshData->MeshletVertexIndexBuffer, &meshData->MeshletVertexIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletVertexIndexBufferOffset, meshHeader->MeshletVertexIndexBufferSizeInBytes); CreateAndUploadDataTemp(&meshData->MeshletTriangleIndexBuffer, &meshData->MeshletTriangleIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletTriangleIndexBufferOffset, meshHeader->MeshletTriangleIndexBufferSizeInBytes); + + applicationPayload->ShaderParameters.VertexBuffer = meshData->VertexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletBuffer = meshData->MeshletBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = meshData->MeshletVertexIndexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = meshData->MeshletTriangleIndexBufferReadDescriptor; } void InitSample(void* payload) @@ -120,14 +145,14 @@ void InitSample(void* payload) applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); + // TODO: For now we create a separate heap to avoid memory management + applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); + // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); LoadMesh(&applicationPayload->TestMeshData, "kitten.mesh", applicationPayload); - applicationPayload->ShaderParameters.VertexBuffer = applicationPayload->TestMeshData.VertexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletBuffer = applicationPayload->TestMeshData.MeshletBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = applicationPayload->TestMeshData.MeshletVertexIndexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = applicationPayload->TestMeshData.MeshletTriangleIndexBufferReadDescriptor; ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -137,7 +162,8 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 }, + .DepthStencilFormat = ElemGraphicsFormat_D32_FLOAT }); ElemFreeShaderLibrary(shaderLibrary); @@ -170,6 +196,9 @@ void FreeSample(void* payload) ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletTriangleIndexBufferReadDescriptor, NULL); ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletTriangleIndexBuffer, NULL); + ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); + ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); + ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); } @@ -272,6 +301,11 @@ void UpdateGameState(GameState* gameState, SampleModelViewerInputActions* inputA void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + if (updateParameters->SizeChanged) + { + CreateDepthBuffer(applicationPayload, updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height); + } SampleModelViewerInputActions* inputActions = &applicationPayload->InputActions; SampleUpdateInputActions(&applicationPayload->InputActionBindings); @@ -319,6 +353,12 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void .LoadAction = ElemRenderPassLoadAction_Clear }}, .Length = 1 + }, + .DepthStencil = + { + .DepthStencil = applicationPayload->DepthBuffer, + .DepthClearValue = 0.0f, + .DepthLoadAction = ElemRenderPassLoadAction_Clear } }); diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index 8213cbac..2263d11b 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -17,17 +17,18 @@ typedef struct uint32_t FaceCount; } ObjMeshElementCount; +typedef struct +{ + uint32_t VertexIndex; + uint32_t TextCoordIndex; + uint32_t NormalIndex; +} ObjMeshVertex; + typedef struct { union { - struct - { - uint32_t VertexIndex; - uint32_t TextCoordIndex; - uint32_t NormalIndex; - }; - + ObjMeshVertex Vertex; uint32_t Indices[3]; } Elements[3]; } ObjMeshFace; @@ -107,7 +108,7 @@ SampleVector2 ReadObjVector2(ElemToolsDataSpan* lineParts, uint32_t linePartCoun }; } -SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCount) +SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCount, bool reverseHandedness) { if (linePartCount < 4) { @@ -119,6 +120,11 @@ SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCoun float y = atof((const char*)lineParts[2].Items); float z = atof((const char*)lineParts[3].Items); + if (reverseHandedness) + { + z = -z; + } + return (SampleVector3) { .X = x, @@ -127,7 +133,7 @@ SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCoun }; } -ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) +ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount, bool reverseHandedness) { if (linePartCount < 4) { @@ -154,10 +160,17 @@ ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount) } } + if (reverseHandedness) + { + ObjMeshVertex temp = result.Elements[1].Vertex; + result.Elements[1] = result.Elements[2]; + result.Elements[2].Vertex = temp; + } + return result; } -InputMeshData ReadObjMesh(ElemToolsDataSpan data) +InputMeshData ReadObjMesh(ElemToolsDataSpan data, bool reverseHandedness) { ObjMeshElementCount meshElementCount = CountObjMeshElements(data); ElemToolsDataSpan line = SampleReadLine(&data); @@ -191,11 +204,11 @@ InputMeshData ReadObjMesh(ElemToolsDataSpan data) } else if (SampleCompareString(lineParts[0], "v")) { - vertexList[vertexCount++] = ReadObjVector3(lineParts, linePartCount); + vertexList[vertexCount++] = ReadObjVector3(lineParts, linePartCount, reverseHandedness); } else if (SampleCompareString(lineParts[0], "vn")) { - normalList[normalCount++] = ReadObjVector3(lineParts, linePartCount); + normalList[normalCount++] = ReadObjVector3(lineParts, linePartCount, reverseHandedness); } else if (SampleCompareString(lineParts[0], "vt")) { @@ -203,17 +216,17 @@ InputMeshData ReadObjMesh(ElemToolsDataSpan data) } else if (SampleCompareString(lineParts[0], "f")) { - ObjMeshFace face = ReadObjFace(lineParts, linePartCount); + ObjMeshFace face = ReadObjFace(lineParts, linePartCount, reverseHandedness); for (uint32_t i = 0; i < 3; i++) { InputMeshVertex vertex = { - .Position = vertexList[face.Elements[i].VertexIndex], - .Normal = normalList[face.Elements[i].NormalIndex], - .TextureCoordinates = textCoordList[face.Elements[i].TextCoordIndex] + .Position = vertexList[face.Elements[i].Vertex.VertexIndex], + .Normal = normalList[face.Elements[i].Vertex.NormalIndex], + .TextureCoordinates = textCoordList[face.Elements[i].Vertex.TextCoordIndex] }; - + inputMeshVertexList[inputMeshVertexCount++] = vertex; } } @@ -289,7 +302,7 @@ int main(int argc, const char* argv[]) return 1; } - InputMeshData inputMeshData = ReadObjMesh(inputData); + InputMeshData inputMeshData = ReadObjMesh(inputData, true); printf("Input mesh vertex Count: %d\n", inputMeshData.VertexCount); ElemVertexBuffer vertexBuffer = @@ -312,15 +325,39 @@ int main(int argc, const char* argv[]) return 1; } - // TODO: This is a good unit test - // TODO: Test cone and bounding box and spheres - for (uint32_t i = 0; i < 10; i++) + // TODO: Good Unit test too! + for (uint32_t i = 0; i < result.Meshlets.Length; i++) { - InputMeshVertex vertex = ((InputMeshVertex*)result.VertexBuffer.Data.Items)[i]; + ElemMeshlet meshlet = result.Meshlets.Items[i]; + + if (i < result.Meshlets.Length - 1) + { + ElemMeshlet nextMeshlet = result.Meshlets.Items[i + 1]; - printf("Vertex X=%f, Y=%f, Z=%f\n", vertex.Position.X, vertex.Position.Y, vertex.Position.Z); + if (meshlet.TriangleOffset + meshlet.TriangleCount - 1 == nextMeshlet.TriangleOffset) + { + printf("Error not last\n"); + } + } + else if (meshlet.TriangleOffset + meshlet.TriangleCount - 1 == result.MeshletTriangleIndexBuffer.Length) + { + printf("ERROR\n"); + } + } + + for (uint32_t i = result.MeshletTriangleIndexBuffer.Length - 10; i < result.MeshletTriangleIndexBuffer.Length + 3; i++) + { + printf("Trig index: %u\n", result.MeshletTriangleIndexBuffer.Items[i]); } + for (uint32_t i = 0; i < result.MeshletTriangleIndexBuffer.Length; i++) + { + if (result.MeshletTriangleIndexBuffer.Items[i] == 0) + { + printf("Zero Triangle index at: %d\n", i); + } + } + printf("Writing mesh data to: %s\n", outputPath); uint32_t meshletBufferSize = result.Meshlets.Length * sizeof(ElemMeshlet); diff --git a/samples/XX-MinimalTriangle/main.c b/samples/XX-MinimalTriangle/main.c index 5bf33408..c5f66495 100644 --- a/samples/XX-MinimalTriangle/main.c +++ b/samples/XX-MinimalTriangle/main.c @@ -72,7 +72,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .TextureFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/src/Elemental/Common/Graphics/ResourceBarrier.h b/src/Elemental/Common/Graphics/ResourceBarrier.h index 84816721..780b256a 100644 --- a/src/Elemental/Common/Graphics/ResourceBarrier.h +++ b/src/Elemental/Common/Graphics/ResourceBarrier.h @@ -10,6 +10,7 @@ typedef ElemHandle ResourceBarrierPool; struct ResourceBarrierItem { ElemGraphicsResourceType Type; + bool IsDepthStencil; ElemGraphicsResource Resource; ElemGraphicsResourceBarrierSyncType BeforeSync; ElemGraphicsResourceBarrierSyncType AfterSync; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 64a636c4..46f6cf89 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -238,7 +238,7 @@ ElemPipelineState VulkanCompileGraphicsPipelineState(ElemGraphicsDevice graphics dynamicState.pDynamicStates = dynamicStates; createInfo.pDynamicState = &dynamicState; - VkFormat formats[] = { ConvertToVulkanTextureFormat(parameters->TextureFormats.Items[0]) }; // TODO: Fill Correct Back Buffer Format + VkFormat formats[] = { ConvertToVulkanTextureFormat(parameters->RenderTargetFormats.Items[0]) }; // TODO: Fill Correct Back Buffer Format VkPipelineRenderingCreateInfo renderingCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; renderingCreateInfo.colorAttachmentCount = 1; // TODO: Change that diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 758bb15e..161d541a 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -380,6 +380,7 @@ typedef enum ElemGraphicsFormat_B8G8R8A8_UNORM, ElemGraphicsFormat_R16G16B16A16_FLOAT, ElemGraphicsFormat_R32G32B32A32_FLOAT, + ElemGraphicsFormat_D32_FLOAT } ElemGraphicsFormat; typedef enum @@ -392,7 +393,8 @@ typedef enum { ElemGraphicsResourceUsage_Read = 0x00, ElemGraphicsResourceUsage_Write = 0x01, - ElemGraphicsResourceUsage_RenderTarget = 0x02 + ElemGraphicsResourceUsage_RenderTarget = 0x02, + ElemGraphicsResourceUsage_DepthStencil = 0x04 } ElemGraphicsResourceUsage; typedef enum @@ -413,7 +415,8 @@ typedef enum ElemGraphicsResourceBarrierAccessType_NoAccess, ElemGraphicsResourceBarrierAccessType_Read, ElemGraphicsResourceBarrierAccessType_Write, - ElemGraphicsResourceBarrierAccessType_RenderTarget + ElemGraphicsResourceBarrierAccessType_RenderTarget, + ElemGraphicsResourceBarrierAccessType_DepthStencilWrite } ElemGraphicsResourceBarrierAccessType; typedef enum @@ -422,6 +425,7 @@ typedef enum ElemGraphicsResourceBarrierLayoutType_Read, ElemGraphicsResourceBarrierLayoutType_Write, ElemGraphicsResourceBarrierLayoutType_RenderTarget, + ElemGraphicsResourceBarrierLayoutType_DepthStencilWrite, ElemGraphicsResourceBarrierLayoutType_Present } ElemGraphicsResourceBarrierLayoutType; @@ -686,7 +690,8 @@ typedef struct // Function name of the pixel shader in the shader library. const char* PixelShaderFunction; // Supported texture formats for the pipeline state. - ElemGraphicsFormatSpan TextureFormats; + ElemGraphicsFormatSpan RenderTargetFormats; + ElemGraphicsFormat DepthStencilFormat; // Optional debug name for the pipeline state. const char* DebugName; } ElemGraphicsPipelineStateParameters; @@ -774,6 +779,16 @@ typedef struct ElemRenderPassStoreAction StoreAction; } ElemRenderPassRenderTarget; +typedef struct +{ + ElemGraphicsResource DepthStencil; + + // TODO: Specify read or write mode ? (write by default) + float DepthClearValue; + ElemRenderPassLoadAction DepthLoadAction; + ElemRenderPassStoreAction DepthStoreAction; +} ElemRenderPassDepthBufferStencil; + /** * Represents a collection of render pass targets. */ @@ -792,6 +807,7 @@ typedef struct { // Render targets to be used in the render pass. ElemRenderPassRenderTargetSpan RenderTargets; + ElemRenderPassDepthBufferStencil DepthStencil; // Viewports to be used in the render pass. ElemViewportSpan Viewports; } ElemBeginRenderPassParameters; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index 862d1968..c8015b3c 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -404,6 +404,7 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions auto resourceDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, DIRECTX12_MAX_RESOURCES); auto rtvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); + auto dsvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); auto rootSignature = CreateDirectX12RootSignature(device); auto handle = SystemAddDataPoolItem(directX12GraphicsDevicePool, { @@ -411,6 +412,7 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions .RootSignature = rootSignature, .ResourceDescriptorHeap = resourceDescriptorHeap, .RTVDescriptorHeap = rtvDescriptorHeap, + .DSVDescriptorHeap = dsvDescriptorHeap, .MemoryArena = memoryArena }); @@ -435,6 +437,7 @@ void DirectX12FreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) FreeDirectX12DescriptorHeap(graphicsDeviceData->ResourceDescriptorHeap); FreeDirectX12DescriptorHeap(graphicsDeviceData->RTVDescriptorHeap); + FreeDirectX12DescriptorHeap(graphicsDeviceData->DSVDescriptorHeap); graphicsDeviceData->Device.Reset(); graphicsDeviceData->RootSignature.Reset(); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h index a8e7e205..112377af 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h @@ -21,6 +21,7 @@ struct DirectX12GraphicsDeviceData uint64_t CommandAllocationGeneration; DirectX12DescriptorHeap ResourceDescriptorHeap; DirectX12DescriptorHeap RTVDescriptorHeap; + DirectX12DescriptorHeap DSVDescriptorHeap; MemoryArena MemoryArena; }; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp index b90fe6a4..635ecb10 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp @@ -5,6 +5,45 @@ #include "DirectX12Resource.h" #include "SystemFunctions.h" +D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE ConvertToDirectX12RenderPassBeginningAccessType(ElemRenderPassLoadAction loadAction) +{ + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE beginAccessType; + + switch (loadAction) + { + case ElemRenderPassLoadAction_Load: + beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; + break; + + case ElemRenderPassLoadAction_Clear: + beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; + break; + + default: + beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD; + break; + } + + return beginAccessType; +} +D3D12_RENDER_PASS_ENDING_ACCESS_TYPE ConvertToDirectX12RenderPassEndingAccessType(ElemRenderPassStoreAction storeAction) +{ + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE endAccessType; + + switch (storeAction) + { + case ElemRenderPassStoreAction_Store: + endAccessType = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; + break; + + default: + endAccessType = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD; + break; + } + + return endAccessType; +} + void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPassParameters* parameters) { // TODO: Check command list type != COMPUTE @@ -35,23 +74,7 @@ void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRender SystemAssert(textureData); // TODO: Validate usage - - D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE beginAccessType; - - switch (renderTargetParameters.LoadAction) - { - case ElemRenderPassLoadAction_Load: - beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; - break; - - case ElemRenderPassLoadAction_Clear: - beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; - break; - - default: - beginAccessType = D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_DISCARD; - break; - } + auto beginAccessType = ConvertToDirectX12RenderPassBeginningAccessType(renderTargetParameters.LoadAction); D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS beginAccessClearValue { @@ -68,18 +91,7 @@ void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRender } }; - D3D12_RENDER_PASS_ENDING_ACCESS_TYPE endAccessType; - - switch (renderTargetParameters.StoreAction) - { - case ElemRenderPassStoreAction_Store: - endAccessType = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; - break; - - default: - endAccessType = D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD; - break; - } + auto endAccessType = ConvertToDirectX12RenderPassEndingAccessType(renderTargetParameters.StoreAction); renderTargetDescList[i] = { @@ -114,13 +126,54 @@ void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRender } } + D3D12_RENDER_PASS_DEPTH_STENCIL_DESC depthStencilDesc = {}; + + if (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) + { + auto depthStencilParameters = parameters->DepthStencil; + + auto textureData = GetDirectX12GraphicsResourceData(depthStencilParameters.DepthStencil); + SystemAssert(textureData); + + // TODO: Validate usage + auto depthBeginAccessType = ConvertToDirectX12RenderPassBeginningAccessType(depthStencilParameters.DepthLoadAction); + auto depthEndAccessType = ConvertToDirectX12RenderPassEndingAccessType(depthStencilParameters.DepthStoreAction); + + D3D12_RENDER_PASS_BEGINNING_ACCESS_CLEAR_PARAMETERS beginAccessClearValue + { + .ClearValue = + { + .Format = textureData->DirectX12Format, + .DepthStencil = { .Depth = depthStencilParameters.DepthClearValue } + } + }; + + depthStencilDesc = + { + .cpuDescriptor = textureData->DsvHandle, + .DepthBeginningAccess = { .Type = depthBeginAccessType, .Clear = beginAccessClearValue }, + .DepthEndingAccess = { .Type = depthEndAccessType } + }; + + ResourceBarrierItem resourceBarrier = + { + .Type = ElemGraphicsResourceType_Texture2D, + .IsDepthStencil = true, + .Resource = depthStencilParameters.DepthStencil, + .AfterAccess = ElemGraphicsResourceBarrierAccessType_DepthStencilWrite, + .AfterLayout = ElemGraphicsResourceBarrierLayoutType_DepthStencilWrite + }; + + EnqueueBarrier(commandListData->ResourceBarrierPool, &resourceBarrier); + } + if (parameters->Viewports.Length > 0) { ElemSetViewports(commandList, parameters->Viewports); } InsertDirectX12ResourceBarriersIfNeeded(commandList, ElemGraphicsResourceBarrierSyncType_RenderTarget); - commandListData->DeviceObject->BeginRenderPass(renderTargetDescList.Length, renderTargetDescList.Pointer, nullptr, D3D12_RENDER_PASS_FLAG_NONE); + commandListData->DeviceObject->BeginRenderPass(renderTargetDescList.Length, renderTargetDescList.Pointer, parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL ? &depthStencilDesc : nullptr, D3D12_RENDER_PASS_FLAG_NONE); } void DirectX12EndRenderPass(ElemCommandList commandList) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 06802d75..f6dace5a 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -73,6 +73,16 @@ DXGI_FORMAT ConvertDirectX12FormatWithoutSrgbIfNeeded(DXGI_FORMAT format) } } +bool CheckDirectX12DepthStencilFormat(ElemGraphicsFormat format) +{ + if (format == ElemGraphicsFormat_D32_FLOAT) + { + return true; + } + + return false; +} + ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ComPtr resource, bool isPresentTexture) { InitDirectX12ResourceMemory(); @@ -82,6 +92,7 @@ ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDev auto resourceDesc = resource->GetDesc(); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = {}; + D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = {}; if (resourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) { @@ -95,10 +106,23 @@ ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDev graphicsDeviceData->Device->CreateRenderTargetView(resource.Get(), &renderTargetViewDesc, rtvHandle); } + else if (resourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + { + dsvHandle = CreateDirectX12DescriptorHandle(graphicsDeviceData->DSVDescriptorHeap); + + D3D12_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc = + { + .Format = ConvertDirectX12FormatToSrgbIfNeeded(resourceDesc.Format), + .ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D, + }; + + graphicsDeviceData->Device->CreateDepthStencilView(resource.Get(), &depthStencilViewDesc, dsvHandle); + } auto handle = SystemAddDataPoolItem(directX12GraphicsResourcePool, { .DeviceObject = resource, .RtvHandle = rtvHandle, + .DsvHandle = dsvHandle, .Type = type, .DirectX12Format = resourceDesc.Format, .DirectX12Flags = resourceDesc.Flags, @@ -131,6 +155,9 @@ DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_R32G32B32A32_FLOAT: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case ElemGraphicsFormat_D32_FLOAT: + return DXGI_FORMAT_D32_FLOAT; + case ElemGraphicsFormat_Raw: return DXGI_FORMAT_UNKNOWN; } @@ -152,6 +179,9 @@ ElemGraphicsFormat ConvertFromDirectX12TextureFormat(DXGI_FORMAT format) case DXGI_FORMAT_R32G32B32A32_FLOAT: return ElemGraphicsFormat_R32G32B32A32_FLOAT; + case DXGI_FORMAT_D32_FLOAT: + return ElemGraphicsFormat_D32_FLOAT; + default: return ElemGraphicsFormat_Raw; } @@ -171,6 +201,11 @@ D3D12_RESOURCE_FLAGS ConvertToDirectX12ResourceFlags(ElemGraphicsResourceUsage u result |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; } + if (usage & ElemGraphicsResourceUsage_DepthStencil) + { + result |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + } + return result; } @@ -187,6 +222,11 @@ ElemGraphicsResourceUsage ConvertFromDirectX12ResourceFlags(D3D12_RESOURCE_FLAGS { result = (ElemGraphicsResourceUsage)(result | ElemGraphicsResourceUsage_RenderTarget); } + + if (flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) + { + result = (ElemGraphicsResourceUsage)(result | ElemGraphicsResourceUsage_DepthStencil); + } return result; } @@ -400,6 +440,18 @@ ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHe return ELEM_HANDLE_NULL; } + if ((resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget) && (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage RenderTarget and DepthStencil should not be used together."); + return ELEM_HANDLE_NULL; + } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil && !CheckDirectX12DepthStencilFormat(resourceInfo->Format)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage DepthStencil should use a compatible format."); + return ELEM_HANDLE_NULL; + } + resourceDescription = CreateDirectX12TextureDescription(resourceInfo); } else @@ -410,11 +462,17 @@ ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHe return ELEM_HANDLE_NULL; } - if ((resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget)) + if (resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget) { SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to RenderTarget."); return ELEM_HANDLE_NULL; } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to DepthStencil."); + return ELEM_HANDLE_NULL; + } initialState = D3D12_BARRIER_LAYOUT_UNDEFINED; resourceDescription = CreateDirectX12BufferDescription(resourceInfo); @@ -559,7 +617,8 @@ ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGra { D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = { - .Format = resourceData->DirectX12Format, + // TODO: Make a function to handle depth stencil formats checks + .Format = resourceData->DirectX12Format == DXGI_FORMAT_D32_FLOAT ? DXGI_FORMAT_R32_FLOAT : resourceData->DirectX12Format, .Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, }; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index 2a1a408d..a298d62e 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -19,6 +19,7 @@ struct DirectX12GraphicsResourceData { ComPtr DeviceObject; D3D12_CPU_DESCRIPTOR_HANDLE RtvHandle; + D3D12_CPU_DESCRIPTOR_HANDLE DsvHandle; ElemGraphicsResourceType Type; DXGI_FORMAT DirectX12Format; D3D12_RESOURCE_FLAGS DirectX12Flags; @@ -42,6 +43,7 @@ DirectX12GraphicsResourceDataFull* GetDirectX12GraphicsResourceDataFull(ElemGrap ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ComPtr resource, bool isPresentTexture); DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format); +bool CheckDirectX12DepthStencilFormat(ElemGraphicsFormat format); ElemGraphicsHeap DirectX12CreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes, const ElemGraphicsHeapOptions* options); void DirectX12FreeGraphicsHeap(ElemGraphicsHeap graphicsHeap); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12ResourceBarrier.cpp b/src/Elemental/Microsoft/Graphics/DirectX12ResourceBarrier.cpp index a4b2a577..73a19b27 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12ResourceBarrier.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12ResourceBarrier.cpp @@ -4,7 +4,7 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -D3D12_BARRIER_SYNC ConvertToDirectX12BarrierSync(ElemGraphicsResourceBarrierSyncType syncType) +D3D12_BARRIER_SYNC ConvertToDirectX12BarrierSync(ElemGraphicsResourceBarrierSyncType syncType, bool isDepthStencil) { switch (syncType) { @@ -15,7 +15,7 @@ D3D12_BARRIER_SYNC ConvertToDirectX12BarrierSync(ElemGraphicsResourceBarrierSync return D3D12_BARRIER_SYNC_COMPUTE_SHADING; case ElemGraphicsResourceBarrierSyncType_RenderTarget: - return D3D12_BARRIER_SYNC_RENDER_TARGET; + return !isDepthStencil ? D3D12_BARRIER_SYNC_RENDER_TARGET : D3D12_BARRIER_SYNC_DEPTH_STENCIL; } } @@ -35,6 +35,9 @@ D3D12_BARRIER_ACCESS ConvertToDirectX12BarrierAccess(ElemGraphicsResourceBarrier case ElemGraphicsResourceBarrierAccessType_RenderTarget: return D3D12_BARRIER_ACCESS_RENDER_TARGET; + case ElemGraphicsResourceBarrierAccessType_DepthStencilWrite: + return D3D12_BARRIER_ACCESS_DEPTH_STENCIL_WRITE; + case ElemGraphicsResourceBarrierAccessType_Write: return D3D12_BARRIER_ACCESS_UNORDERED_ACCESS; } @@ -60,6 +63,9 @@ D3D12_BARRIER_LAYOUT ConvertToDirectX12BarrierLayout(ElemGraphicsResourceBarrier case ElemGraphicsResourceBarrierLayoutType_RenderTarget: return D3D12_BARRIER_LAYOUT_RENDER_TARGET; + case ElemGraphicsResourceBarrierLayoutType_DepthStencilWrite: + return D3D12_BARRIER_LAYOUT_DEPTH_STENCIL_WRITE; + case ElemGraphicsResourceBarrierLayoutType_Present: return D3D12_BARRIER_LAYOUT_PRESENT; } @@ -107,8 +113,8 @@ void InsertDirectX12ResourceBarriersIfNeeded(ElemCommandList commandList, ElemGr directX12BufferBarrier->pResource = graphicsResourceData->DeviceObject.Get(); directX12BufferBarrier->Size = graphicsResourceData->Width; - directX12BufferBarrier->SyncBefore = ConvertToDirectX12BarrierSync(barrier.BeforeSync); - directX12BufferBarrier->SyncAfter = ConvertToDirectX12BarrierSync(barrier.AfterSync); + directX12BufferBarrier->SyncBefore = ConvertToDirectX12BarrierSync(barrier.BeforeSync, false); + directX12BufferBarrier->SyncAfter = ConvertToDirectX12BarrierSync(barrier.AfterSync, false); directX12BufferBarrier->AccessBefore = ConvertToDirectX12BarrierAccess(barrier.BeforeAccess); directX12BufferBarrier->AccessAfter = ConvertToDirectX12BarrierAccess(barrier.AfterAccess); } @@ -119,11 +125,11 @@ void InsertDirectX12ResourceBarriersIfNeeded(ElemCommandList commandList, ElemGr auto directX12TextureBarriers = SystemPushArray(stackMemoryArena, barriersInfo.TextureBarriers.Length); D3D12_BARRIER_GROUP directX12TextureBarriersGroup = - { - .Type = D3D12_BARRIER_TYPE_TEXTURE, - .NumBarriers = (uint32_t)barriersInfo.TextureBarriers.Length, - .pTextureBarriers = directX12TextureBarriers.Pointer - }; + { + .Type = D3D12_BARRIER_TYPE_TEXTURE, + .NumBarriers = (uint32_t)barriersInfo.TextureBarriers.Length, + .pTextureBarriers = directX12TextureBarriers.Pointer + }; barrierGroups[barrierGroupCount++] = directX12TextureBarriersGroup; @@ -136,8 +142,8 @@ void InsertDirectX12ResourceBarriersIfNeeded(ElemCommandList commandList, ElemGr SystemAssert(graphicsResourceData); directX12TextureBarrier->pResource = graphicsResourceData->DeviceObject.Get(); - directX12TextureBarrier->SyncBefore = ConvertToDirectX12BarrierSync(barrier.BeforeSync); - directX12TextureBarrier->SyncAfter = ConvertToDirectX12BarrierSync(barrier.AfterSync); + directX12TextureBarrier->SyncBefore = ConvertToDirectX12BarrierSync(barrier.BeforeSync, barrier.IsDepthStencil); + directX12TextureBarrier->SyncAfter = ConvertToDirectX12BarrierSync(barrier.AfterSync, barrier.IsDepthStencil); directX12TextureBarrier->AccessBefore = ConvertToDirectX12BarrierAccess(barrier.BeforeAccess); directX12TextureBarrier->AccessAfter = ConvertToDirectX12BarrierAccess(barrier.AfterAccess); directX12TextureBarrier->LayoutBefore = ConvertToDirectX12BarrierLayout(barrier.BeforeLayout); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 879fe890..61ab82e6 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -147,15 +147,14 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev D3D12_RT_FORMAT_ARRAY renderTargets = {}; renderTargets.NumRenderTargets = 1; - renderTargets.RTFormats[0] = ConvertToDirectX12TextureFormat(parameters->TextureFormats.Items[0]); // TODO: Fill Correct Back Buffer Format + renderTargets.RTFormats[0] = ConvertToDirectX12TextureFormat(parameters->RenderTargetFormats.Items[0]); // TODO: Fill Correct Back Buffer Format DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN; - /*if (renderPassDescriptor.DepthTexturePointer.HasValue) + if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) { - // TODO: Change that - depthFormat = DXGI_FORMAT_D32_FLOAT; - }*/ + depthFormat = ConvertToDirectX12TextureFormat(parameters->DepthStencilFormat); + } DXGI_SAMPLE_DESC sampleDesc = {}; sampleDesc.Count = 1; @@ -176,10 +175,18 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev rasterizerState.ForcedSampleCount = 0; rasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - /* + // TODO: With Unit tests D3D12_DEPTH_STENCIL_DESC depthStencilState = {}; + + // HACK: Temporary + if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) + { + depthStencilState.DepthEnable = true; + depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER; + } - if (renderPassDescriptor.DepthBufferOperation != GraphicsDepthBufferOperation::DepthNone) + /*if (renderPassDescriptor.DepthBufferOperation != GraphicsDepthBufferOperation::DepthNone) { depthStencilState.DepthEnable = true; depthStencilState.StencilEnable = false; @@ -199,8 +206,8 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev { depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER; } - } -*/ + }*/ + D3D12_BLEND_DESC blendState = {}; blendState.AlphaToCoverageEnable = false; blendState.IndependentBlendEnable = false; @@ -260,7 +267,7 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev psoDesc.SampleDesc = sampleDesc; psoDesc.RasterizerState = rasterizerState; psoDesc.DepthStencilFormat = depthFormat; - //psoDesc.DepthStencilState = depthStencilState; + psoDesc.DepthStencilState = depthStencilState; psoDesc.BlendState = blendState; psoStream.SizeInBytes = sizeof(GraphicsPso); diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Common/DirectXShaderCompiler.cpp index b082a6b4..50091b55 100644 --- a/src/ElementalTools/Common/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Common/DirectXShaderCompiler.cpp @@ -132,6 +132,7 @@ ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn { parameters[parameterIndex++] = L"-spirv"; parameters[parameterIndex++] = L"-fspv-target-env=vulkan1.3"; + parameters[parameterIndex++] = L"-fvk-use-dx-layout"; } parameters[parameterIndex++] = L"-HV"; diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp index 8e1cce85..729ee687 100644 --- a/src/ElementalTools/Common/MeshletBuilder.cpp +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -6,7 +6,6 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf // TODO: Error messages auto stackMemoryArena = SystemGetStackMemoryArena(); - printf("Meshlet Builder\n"); auto indexCount = vertexBuffer.Data.Length / vertexBuffer.VertexSize; auto vertexRemap = SystemPushArrayZero(stackMemoryArena, indexCount); @@ -87,7 +86,6 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf for (uint32_t j = 0; j < meshlet.triangle_count; j++) { - printf("Processing Triangle: %d (meshlet %d)\n", j, i); auto pointer = &meshletTriangleIndexListRaw[meshlet.triangle_offset + j * 3]; auto p0 = pointer[0]; @@ -98,11 +96,9 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf } meshletVertexIndexCount += meshlet.vertex_count; - meshletTriangleIndexCount += meshlet.triangle_count; + meshletTriangleIndexCount = max(meshletTriangleIndexCount, meshlet.triangle_offset / 3 + meshlet.triangle_count); } - printf("Done\n"); - return { .MeshletMaxVertexCount = meshletMaxVertexCount, diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index 0724bccb..73ad7cfe 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -308,7 +308,7 @@ ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const ch .ShaderLibrary = shaderLibrary, .MeshShaderFunction = meshShaderFunction, .PixelShaderFunction = pixelShaderFunction, - .TextureFormats = { .Items = &renderTargetFormat, .Length = 1 } + .RenderTargetFormats = { .Items = &renderTargetFormat, .Length = 1 } }; auto pipelineState = ElemCompileGraphicsPipelineState(graphicsDevice, &pipelineStateParameters); @@ -353,13 +353,19 @@ void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer) ElemFreeGraphicsHeap(gpuBuffer.GraphicsHeap); } -TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format) +TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage) { - auto textureInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, 1, format, (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_Write | ElemGraphicsResourceUsage_RenderTarget), nullptr); + auto textureInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, 1, format, usage, nullptr); auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, textureInfo.SizeInBytes, nullptr); auto texture = ElemCreateGraphicsResource(graphicsHeap, 0, &textureInfo); auto textureReadDescriptor = ElemCreateGraphicsResourceDescriptor(texture, ElemGraphicsResourceDescriptorUsage_Read, nullptr); - auto textureWriteDescriptor = ElemCreateGraphicsResourceDescriptor(texture, ElemGraphicsResourceDescriptorUsage_Write, nullptr); + + auto textureWriteDescriptor = -1; + + if (usage & ElemGraphicsResourceUsage_Write) + { + textureWriteDescriptor = ElemCreateGraphicsResourceDescriptor(texture, ElemGraphicsResourceDescriptorUsage_Write, nullptr); + } return { @@ -374,7 +380,12 @@ TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t void TestFreeGpuTexture(TestGpuTexture texture) { ElemFreeGraphicsResourceDescriptor(texture.ReadDescriptor, nullptr); - ElemFreeGraphicsResourceDescriptor(texture.WriteDescriptor, nullptr); + + if (texture.WriteDescriptor != -1) + { + ElemFreeGraphicsResourceDescriptor(texture.WriteDescriptor, nullptr); + } + ElemFreeGraphicsResource(texture.Texture, nullptr); ElemFreeGraphicsHeap(texture.GraphicsHeap); } @@ -407,28 +418,6 @@ void TestDispatchComputeForReadbackBuffer(ElemGraphicsDevice graphicsDevice, Ele ElemFreePipelineState(pipelineState); } -void TestBeginClearRenderPass(ElemCommandList commandList, ElemGraphicsResource renderTarget, ElemColor clearColor) -{ - ElemRenderPassRenderTarget renderPassRenderTarget = - { - .RenderTarget = renderTarget, - .ClearColor = clearColor, - .LoadAction = ElemRenderPassLoadAction_Clear - }; - - ElemBeginRenderPassParameters parameters = - { - .RenderTargets = - { - .Items = &renderPassRenderTarget, - .Length = 1 - } - }; - - // Act - ElemBeginRenderPass(commandList, ¶meters); -} - void TestBarrierCheckSyncTypeToString(char* destination, ElemGraphicsResourceBarrierSyncType syncType) { switch (syncType) diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 1c22e2bf..e1f7a6d4 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -133,7 +133,7 @@ ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const ch TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_Gpu); void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer); -TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format); +TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage); void TestFreeGpuTexture(TestGpuTexture texture); template @@ -142,8 +142,6 @@ void TestDispatchCompute(ElemCommandList commandList, ElemPipelineState pipeline template void TestDispatchComputeForReadbackBuffer(ElemGraphicsDevice graphicsDevice, ElemCommandQueue commandQueue, const char* shaderName, const char* function, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, const T* parameters); -void TestBeginClearRenderPass(ElemCommandList commandList, ElemGraphicsResource renderTarget, ElemColor clearColor); - void TestBarrierCheckSyncTypeToString(char* destination, ElemGraphicsResourceBarrierSyncType syncType); void TestBarrierCheckAccessTypeToString(char* destination, ElemGraphicsResourceBarrierAccessType accessType); bool TestDebugLogBarrier(const TestBarrierCheck* check, char* expectedMessage, uint32_t messageLength); diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index d939a807..19520417 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -3,6 +3,7 @@ #include "utest.h" // TODO: Test Viewports +// TODO: Test Multiple render targets // TODO: Render on a render target texture // TODO: Check command list type when dispatch mesh // TODO: Multiple config for rendering @@ -15,10 +16,26 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT); + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); // Act - TestBeginClearRenderPass(commandList, renderTarget.Texture, { .Red = 1.0f, .Green = 0.5f, .Blue = 0.25f, .Alpha = 0.95f }); + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 1.0f, .Green = 0.5f, .Blue = 0.25f, .Alpha = 0.95f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); ElemEndRenderPass(commandList); // Assert @@ -40,7 +57,7 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto floatData = (float*)bufferData.Items; - for (uint32_t i = 0; i < bufferData.Length / 4; i += 4) + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) { ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); ASSERT_EQ_MSG(floatData[i + 1], 0.5f, "Green channel data is invalid."); @@ -49,6 +66,54 @@ UTEST(Rendering, RenderPassClearRenderTarget) } } +UTEST(Rendering, RenderPassClearDepthBuffer) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + + // Act + ElemBeginRenderPassParameters parameters = + { + .DepthStencil = + { + .DepthStencil = depthBuffer.Texture, + .DepthClearValue = 0.5f, + .DepthLoadAction = ElemRenderPassLoadAction_Clear + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)depthBuffer.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTextureFloat", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(depthBuffer); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto floatData = (float*)bufferData.Items; + + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i++) + { + ASSERT_EQ_MSG(floatData[i], 0.5f, "Depth data is invalid."); + } +} + UTEST(Rendering, DispatchMesh) { // Arrange @@ -56,11 +121,27 @@ UTEST(Rendering, DispatchMesh) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT); + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", renderTarget.Format); // Act - TestBeginClearRenderPass(commandList, renderTarget.Texture, { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }); + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); ElemBindPipelineState(commandList, meshShaderPipeline); ElemDispatchMesh(commandList, 1, 1, 1); ElemEndRenderPass(commandList); @@ -85,7 +166,7 @@ UTEST(Rendering, DispatchMesh) auto floatData = (float*)bufferData.Items; - for (uint32_t i = 0; i < bufferData.Length / 4; i += 4) + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) { ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); ASSERT_EQ_MSG(floatData[i + 1], 1.0f, "Green channel data is invalid."); @@ -93,3 +174,5 @@ UTEST(Rendering, DispatchMesh) ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); } } + +// TODO: Test Depth buffer (Clear with a specific value and rendering) diff --git a/tests/GraphicsTests/ResourceBarrierTests.cpp b/tests/GraphicsTests/ResourceBarrierTests.cpp index 9c77b06d..44009fc2 100644 --- a/tests/GraphicsTests/ResourceBarrierTests.cpp +++ b/tests/GraphicsTests/ResourceBarrierTests.cpp @@ -235,7 +235,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureReadAfterWrite) auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT); + auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT, (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_RenderTarget | ElemGraphicsResourceUsage_Write)); auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, width * height * 4 * sizeof(float), ElemGraphicsHeapType_Readback); auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); @@ -309,7 +309,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT); + auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT, (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_RenderTarget | ElemGraphicsResourceUsage_Write)); auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, width * height * 4 * sizeof(float), ElemGraphicsHeapType_Readback); auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); @@ -329,7 +329,23 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) ElemGraphicsResourceBarrierLayoutType_Undefined, ElemGraphicsResourceBarrierLayoutType_Write) )); - TestBeginClearRenderPass(commandList, gpuTexture.Texture, { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }); + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = gpuTexture.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); ElemBindPipelineState(commandList, meshShaderPipelineState); ElemDispatchMesh(commandList, 1, 1, 1); ElemEndRenderPass(commandList); @@ -387,6 +403,8 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) } } +// TODO: DepthBuffer + UTEST(ResourceBarrier, GraphicsResourceBarrier_BufferReadAfterWriteWithCustomBeforeSyncAndAccess) { // Arrange @@ -518,7 +536,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureReadAfterWriteWithCustomBe auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT); + auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT, (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_RenderTarget | ElemGraphicsResourceUsage_Write)); auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, width * height * 4 * sizeof(float), ElemGraphicsHeapType_Readback); auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); @@ -590,7 +608,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureReadAfterWriteWithCustomAf auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT); + auto gpuTexture = TestCreateGpuTexture(graphicsDevice, width, height, ElemGraphicsFormat_R16G16B16A16_FLOAT, (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_RenderTarget | ElemGraphicsResourceUsage_Write)); auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, width * height * 4 * sizeof(float), ElemGraphicsHeapType_Readback); auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index ffbabb36..b7cdad67 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -115,6 +115,30 @@ UTEST(Resource, CreateBufferGraphicsResource_UsageRenderTargetAndWrite) ASSERT_EQ_MSG(resource, ELEM_HANDLE_NULL, "Handle should be null."); } +UTEST(Resource, CreateBufferGraphicsResource_UsageDepthStencil) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + + ElemGraphicsResourceInfo resourceInfo = + { + .Type = ElemGraphicsResourceType_Buffer, + .Width = 64, + .Usage = ElemGraphicsResourceUsage_DepthStencil + }; + + // Act + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Assert + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("GraphicsBuffer usage should not be equals to DepthStencil."); + ASSERT_EQ_MSG(resource, ELEM_HANDLE_NULL, "Handle should be null."); +} + UTEST(Resource, CreateGraphicsBufferResourceInfo) { // Arrange @@ -181,6 +205,150 @@ UTEST(Resource, CreateTexture2DGraphicsResource) ASSERT_EQ_MSG(resourceInfo.Usage, ElemGraphicsResourceUsage_Read, "Usage should match the creation usage."); } +UTEST(Resource, CreateTexture2DGraphicsResource_UsageRenderTarget) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto width = 128u; + auto height = 256u; + auto format = ElemGraphicsFormat_B8G8R8A8_SRGB; + auto mipLevels = 3u; + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + + ElemGraphicsResourceInfo resourceInfo = + { + .Type = ElemGraphicsResourceType_Texture2D, + .Width = width, + .Height = height, + .MipLevels = mipLevels, + .Format = format, + .Usage = ElemGraphicsResourceUsage_RenderTarget + }; + + // Act + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Assert + resourceInfo = ElemGetGraphicsResourceInfo(resource); + + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_NE_MSG(resource, ELEM_HANDLE_NULL, "Handle should not be null."); + + ASSERT_EQ_MSG(resourceInfo.Type, ElemGraphicsResourceType_Texture2D, "Resource Type should be a Texture2D."); + ASSERT_EQ_MSG(resourceInfo.Width, width, "Width should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Height, height, "Height should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.MipLevels, mipLevels, "MipLevels should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Format, format, "Format should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Usage, ElemGraphicsResourceUsage_RenderTarget, "Usage should match the creation usage."); +} + +UTEST(Resource, CreateTexture2DGraphicsResource_UsageDepthStencil) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto width = 128u; + auto height = 256u; + auto format = ElemGraphicsFormat_D32_FLOAT; + auto mipLevels = 3u; + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + + ElemGraphicsResourceInfo resourceInfo = + { + .Type = ElemGraphicsResourceType_Texture2D, + .Width = width, + .Height = height, + .MipLevels = mipLevels, + .Format = format, + .Usage = ElemGraphicsResourceUsage_DepthStencil + }; + + // Act + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Assert + resourceInfo = ElemGetGraphicsResourceInfo(resource); + + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_NE_MSG(resource, ELEM_HANDLE_NULL, "Handle should not be null."); + + ASSERT_EQ_MSG(resourceInfo.Type, ElemGraphicsResourceType_Texture2D, "Resource Type should be a Texture2D."); + ASSERT_EQ_MSG(resourceInfo.Width, width, "Width should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Height, height, "Height should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.MipLevels, mipLevels, "MipLevels should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Format, format, "Format should be equals to creation."); + ASSERT_EQ_MSG(resourceInfo.Usage, ElemGraphicsResourceUsage_DepthStencil, "Usage should match the creation usage."); +} + +UTEST(Resource, CreateTexture2DGraphicsResource_UsageDepthStencil_WrongFormat) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto width = 128u; + auto height = 256u; + auto format = ElemGraphicsFormat_B8G8R8A8_SRGB; + auto mipLevels = 3u; + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + + ElemGraphicsResourceInfo resourceInfo = + { + .Type = ElemGraphicsResourceType_Texture2D, + .Width = width, + .Height = height, + .MipLevels = mipLevels, + .Format = format, + .Usage = ElemGraphicsResourceUsage_DepthStencil + }; + + // Act + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Assert + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("Texture2D with usage DepthStencil should use a compatible format."); + ASSERT_EQ_MSG(resource, ELEM_HANDLE_NULL, "Handle should be null."); +} + +UTEST(Resource, CreateTexture2DGraphicsResource_UsageRenderTargetDepthStencil) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto width = 128u; + auto height = 256u; + auto format = ElemGraphicsFormat_B8G8R8A8_SRGB; + auto mipLevels = 3u; + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + + ElemGraphicsResourceInfo resourceInfo = + { + .Type = ElemGraphicsResourceType_Texture2D, + .Width = width, + .Height = height, + .MipLevels = mipLevels, + .Format = format, + .Usage = (ElemGraphicsResourceUsage)(ElemGraphicsResourceUsage_RenderTarget | ElemGraphicsResourceUsage_DepthStencil) + }; + + // Act + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Assert + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("Texture2D with usage RenderTarget and DepthStencil should not be used together."); + ASSERT_EQ_MSG(resource, ELEM_HANDLE_NULL, "Handle should be null."); +} + UTEST(Resource, CreateTexture2DGraphicsResource_WidthZero) { // Arrange diff --git a/tests/GraphicsTests/Shaders/RenderingTests.hlsl b/tests/GraphicsTests/Shaders/RenderingTests.hlsl index 56197453..47323ba6 100644 --- a/tests/GraphicsTests/Shaders/RenderingTests.hlsl +++ b/tests/GraphicsTests/Shaders/RenderingTests.hlsl @@ -18,6 +18,16 @@ void CopyTexture(uint2 threadId: SV_DispatchThreadID) destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); } +[shader("compute")] +[numthreads(16, 16, 1)] +void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) +{ + Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; + RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; + + destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); +} + struct VertexOutput { float4 Position: SV_Position; diff --git a/tests/ToolsTests/MeshBuilderTests.cpp b/tests/ToolsTests/MeshBuilderTests.cpp new file mode 100644 index 00000000..9a532a4e --- /dev/null +++ b/tests/ToolsTests/MeshBuilderTests.cpp @@ -0,0 +1,135 @@ +#include "ToolsTests.h" +#include "utest.h" + +// TODO: Add check when passing only vertex buffer for mod 3 +// TODO: Test cone and bounding box and spheres + +struct TestMeshVector2 +{ + float X, Y; +}; + +struct TestMeshVector3 +{ + float X, Y, Z; +}; + +struct TestMeshVertex +{ + TestMeshVector3 Position; + TestMeshVector3 Normal; + TestMeshVector2 TextureCoordinates; +}; + +ElemVertexBuffer TestBuildVertexBuffer(TestMeshVertex* destination, uint32_t count) +{ + float globalVertexCounter = 0u; + + for (uint32_t i = 0; i < count; i++) + { + destination[i] = + { + .Position = { globalVertexCounter++, globalVertexCounter++, globalVertexCounter++ }, + .Normal = { globalVertexCounter++, globalVertexCounter++, globalVertexCounter++ }, + .TextureCoordinates = { globalVertexCounter++, globalVertexCounter++ } + }; + } + + return + { + .Data = { .Items = (uint8_t*)destination, .Length = (uint32_t)(count * sizeof(TestMeshVertex)) }, + .VertexSize = sizeof(TestMeshVertex) + }; +} + +UTEST(MeshBuilder, CheckVertexBuffer) +{ + // Arrange + const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount); + + // Act + auto result = ElemBuildMeshlets(vertexBuffer, NULL); + + // Assert + ASSERT_FALSE(result.HasErrors); + ASSERT_EQ_MSG(result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize, vertexCount, "Vertex Count is not correct."); + + for (uint32_t i = 0; i < vertexCount; i++) + { + auto found = false; + auto testVertex = vertexList[i]; + + for (uint32_t j = 0; j < result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize; j++) + { + auto resultVertex = (TestMeshVertex*)&result.VertexBuffer.Data.Items[j * result.VertexBuffer.VertexSize]; + + if (testVertex.Position.X == resultVertex->Position.X && + testVertex.Position.Y == resultVertex->Position.Y && + testVertex.Position.Z == resultVertex->Position.Z && + testVertex.Normal.X == resultVertex->Normal.X && + testVertex.Normal.Y == resultVertex->Normal.Y && + testVertex.Normal.Z == resultVertex->Normal.Z && + testVertex.TextureCoordinates.X == resultVertex->TextureCoordinates.X && + testVertex.TextureCoordinates.Y == resultVertex->TextureCoordinates.Y) + { + found = true; + break; + } + } + + ASSERT_TRUE(found); + } +} + +UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) +{ + // Arrange + const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + + for (uint32_t i = 0; i < 3; i++) + { + vertexList[vertexCount - 3 + i] = vertexList[vertexCount - 6 + i]; + } + + vertexBuffer.Data.Length += vertexBuffer.VertexSize * 3; + + // Act + auto result = ElemBuildMeshlets(vertexBuffer, NULL); + + // Assert + ASSERT_FALSE(result.HasErrors); + ASSERT_EQ_MSG(result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize, vertexCount - 3, "Vertex Count is not correct."); +} + +UTEST(MeshBuilder, CheckMeshletTriangleIndexBuffer) +{ + // Arrange + const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + + // Act + auto result = ElemBuildMeshlets(vertexBuffer, NULL); + + // Assert + ASSERT_FALSE(result.HasErrors); + + for (uint32_t i = 0; i < result.Meshlets.Length; i++) + { + ElemMeshlet meshlet = result.Meshlets.Items[i]; + + if (i < result.Meshlets.Length - 1) + { + ElemMeshlet nextMeshlet = result.Meshlets.Items[i + 1]; + ASSERT_NE_MSG(meshlet.TriangleOffset + meshlet.TriangleCount - 1, nextMeshlet.TriangleOffset, "Error Not Last"); + } + else + { + ASSERT_NE_MSG(meshlet.TriangleOffset + meshlet.TriangleCount - 1, result.MeshletTriangleIndexBuffer.Length, "Error"); + } + } +} diff --git a/tests/ToolsTests/UnityBuild.cpp b/tests/ToolsTests/UnityBuild.cpp index f29003d4..67835e3c 100644 --- a/tests/ToolsTests/UnityBuild.cpp +++ b/tests/ToolsTests/UnityBuild.cpp @@ -1,4 +1,5 @@ #include "ShaderCompilerTests.cpp" +#include "MeshBuilderTests.cpp" #include "utest.h" UTEST_MAIN(); From a7f703269cc0d645f7af1749ea25cb3684561ca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 19 Aug 2024 06:16:17 +0200 Subject: [PATCH 08/93] Add unit tests for depth compare --- .../Microsoft/Graphics/DirectX12Resource.cpp | 2 + .../Microsoft/Graphics/DirectX12Shader.cpp | 10 +- src/ElementalTools/ElementalTools.h | 2 + tests/GraphicsTests/GraphicsTests.cpp | 14 +- tests/GraphicsTests/GraphicsTests.h | 2 +- tests/GraphicsTests/RenderingTests.cpp | 8 +- tests/GraphicsTests/ResourceBarrierTests.cpp | 8 +- tests/GraphicsTests/ShaderTests.cpp | 152 ++++++++++++++++++ tests/ToolsTests/MeshBuilderTests.cpp | 29 ++++ 9 files changed, 211 insertions(+), 16 deletions(-) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index f6dace5a..2ef7a4c1 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -416,6 +416,8 @@ ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHe SystemAssert(graphicsDeviceData); D3D12_BARRIER_LAYOUT initialState = D3D12_BARRIER_LAYOUT_COMMON; + + // TODO: Handle clear value D3D12_CLEAR_VALUE* clearValue = nullptr; D3D12_RESOURCE_DESC1 resourceDescription = {}; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 61ab82e6..71183352 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -142,7 +142,6 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev auto shaderLibraryData= GetDirectX12ShaderLibraryData(parameters->ShaderLibrary); SystemAssert(shaderLibraryData); - ComPtr pipelineState; D3D12_RT_FORMAT_ARRAY renderTargets = {}; @@ -179,12 +178,12 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev D3D12_DEPTH_STENCIL_DESC depthStencilState = {}; // HACK: Temporary - if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) + /*if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) { depthStencilState.DepthEnable = true; depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER; - } + }*/ /*if (renderPassDescriptor.DepthBufferOperation != GraphicsDepthBufferOperation::DepthNone) { @@ -264,15 +263,16 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev } psoDesc.RenderTargets = renderTargets; - psoDesc.SampleDesc = sampleDesc; - psoDesc.RasterizerState = rasterizerState; psoDesc.DepthStencilFormat = depthFormat; psoDesc.DepthStencilState = depthStencilState; + psoDesc.RasterizerState = rasterizerState; psoDesc.BlendState = blendState; + psoDesc.SampleDesc = sampleDesc; psoStream.SizeInBytes = sizeof(GraphicsPso); psoStream.pPipelineStateSubobjectStream = &psoDesc; + ComPtr pipelineState; AssertIfFailed(graphicsDeviceData->Device->CreatePipelineState(&psoStream, IID_PPV_ARGS(pipelineState.GetAddressOf()))); return pipelineState; diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 659a3c63..93495d9e 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -236,6 +236,8 @@ typedef struct ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); +// TODO: Do an optimize mesh function that can be used to optimize a normal mesh (for Raytracing for example) + #ifdef UseToolsLoader #ifndef ElementalToolsLoader #include "ElementalToolsLoader.c" diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index 73ad7cfe..1422654b 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -299,17 +299,15 @@ ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const return pipelineState; } -ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, ElemGraphicsFormat renderTargetFormat) +ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) { auto shaderLibrary = TestOpenShader(graphicsDevice, shader); - ElemGraphicsPipelineStateParameters pipelineStateParameters = - { - .ShaderLibrary = shaderLibrary, - .MeshShaderFunction = meshShaderFunction, - .PixelShaderFunction = pixelShaderFunction, - .RenderTargetFormats = { .Items = &renderTargetFormat, .Length = 1 } - }; + ElemGraphicsPipelineStateParameters pipelineStateParameters = *baseParameters; + + pipelineStateParameters.ShaderLibrary = shaderLibrary; + pipelineStateParameters.MeshShaderFunction = meshShaderFunction; + pipelineStateParameters.PixelShaderFunction = pixelShaderFunction; auto pipelineState = ElemCompileGraphicsPipelineState(graphicsDevice, &pipelineStateParameters); diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index e1f7a6d4..f01db9a4 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -128,7 +128,7 @@ void TestInitLog(); ElemShaderLibrary TestOpenShader(ElemGraphicsDevice graphicsDevice, const char* shader); ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* function); -ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, ElemGraphicsFormat renderTargetFormat); +ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_Gpu); void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer); diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 19520417..a9a4a8fb 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -122,7 +122,13 @@ UTEST(Rendering, DispatchMesh) auto commandList = ElemGetCommandList(commandQueue, nullptr); auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", renderTarget.Format); + + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); // Act ElemRenderPassRenderTarget renderPassRenderTarget = diff --git a/tests/GraphicsTests/ResourceBarrierTests.cpp b/tests/GraphicsTests/ResourceBarrierTests.cpp index 44009fc2..ae60c8cc 100644 --- a/tests/GraphicsTests/ResourceBarrierTests.cpp +++ b/tests/GraphicsTests/ResourceBarrierTests.cpp @@ -314,7 +314,13 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); auto readTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestReadTextureData"); - auto meshShaderPipelineState = TestOpenMeshShader(graphicsDevice, "ResourceBarrierTests.shader", "TestMeshShader", "TestPixelShader", gpuTexture.Format); + + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargetFormats = { .Items = &gpuTexture.Format, .Length = 1 } + }; + + auto meshShaderPipelineState = TestOpenMeshShader(graphicsDevice, "ResourceBarrierTests.shader", "TestMeshShader", "TestPixelShader", &psoParameters); // Act auto commandList = ElemGetCommandList(commandQueue, nullptr); diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index 0af9ab23..eccae5f0 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -99,3 +99,155 @@ UTEST(Shader, DispatchCompute) ASSERT_EQ_MSG(uintData[i], i < 16 ? i : 0u, "Compute shader data is invalid."); } } + +UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 }, + .DepthStencilFormat = depthBuffer.Format + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + }, + .DepthStencil = + { + .DepthStencil = depthBuffer.Texture, + .DepthClearValue = 0.0f, + .DepthLoadAction = ElemRenderPassLoadAction_Clear + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + TestFreeGpuTexture(depthBuffer); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto floatData = (float*)bufferData.Items; + + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) + { + ASSERT_EQ_MSG(floatData[i], 0.0f, "Red channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 1], 0.0f, "Green channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 2], 1.0f, "Blue channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); + } +} + +UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreaterOrEquals) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 }, + .DepthStencilFormat = depthBuffer.Format + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + }, + .DepthStencil = + { + .DepthStencil = depthBuffer.Texture, + .DepthClearValue = 0.0f, + .DepthLoadAction = ElemRenderPassLoadAction_Clear + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + TestFreeGpuTexture(depthBuffer); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto floatData = (float*)bufferData.Items; + + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) + { + ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 1], 1.0f, "Green channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 2], 0.0f, "Blue channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); + } +} diff --git a/tests/ToolsTests/MeshBuilderTests.cpp b/tests/ToolsTests/MeshBuilderTests.cpp index 9a532a4e..a427f2a5 100644 --- a/tests/ToolsTests/MeshBuilderTests.cpp +++ b/tests/ToolsTests/MeshBuilderTests.cpp @@ -105,6 +105,35 @@ UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) ASSERT_EQ_MSG(result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize, vertexCount - 3, "Vertex Count is not correct."); } +UTEST(MeshBuilder, CheckMeshletVertexIndexBuffer) +{ + // Arrange + const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + + // Act + auto result = ElemBuildMeshlets(vertexBuffer, NULL); + + // Assert + ASSERT_FALSE(result.HasErrors); + + for (uint32_t i = 0; i < result.Meshlets.Length; i++) + { + ElemMeshlet meshlet = result.Meshlets.Items[i]; + + if (i < result.Meshlets.Length - 1) + { + ElemMeshlet nextMeshlet = result.Meshlets.Items[i + 1]; + ASSERT_NE_MSG(meshlet.VertexIndexOffset + meshlet.VertexIndexCount - 1, nextMeshlet.VertexIndexOffset, "Error Not Last"); + } + else + { + ASSERT_NE_MSG(meshlet.VertexIndexOffset + meshlet.VertexIndexCount - 1, result.MeshletVertexIndexBuffer.Length, "Error"); + } + } +} + UTEST(MeshBuilder, CheckMeshletTriangleIndexBuffer) { // Arrange From 9cc0763fc5e4bd5e25ef167fa892597f83c18b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 19 Aug 2024 15:19:09 +0200 Subject: [PATCH 09/93] Add Pso Depth tests --- samples/Elemental/05-HelloMesh/main.c | 8 +- src/Elemental/Elemental.h | 23 ++- .../Microsoft/Graphics/DirectX12Shader.cpp | 58 +++--- .../Common/DirectXShaderCompiler.cpp | 2 +- tests/GraphicsTests/RenderingTests.cpp | 6 +- tests/GraphicsTests/ShaderTests.cpp | 170 ++++++++++-------- tests/GraphicsTests/Shaders/Assert.hlsl | 75 ++++++++ .../GraphicsTests/Shaders/RenderingTests.hlsl | 30 ---- tests/GraphicsTests/Shaders/ShaderTests.hlsl | 47 +++++ 9 files changed, 276 insertions(+), 143 deletions(-) create mode 100644 tests/GraphicsTests/Shaders/Assert.hlsl diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index 9bc3ece1..11fcd462 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -163,7 +163,9 @@ void InitSample(void* payload) .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 }, - .DepthStencilFormat = ElemGraphicsFormat_D32_FLOAT + .DepthStencilFormat = ElemGraphicsFormat_D32_FLOAT, + .DepthWrite = true, + .DepthCompareFunction = ElemGraphicsCompareFunction_Greater }); ElemFreeShaderLibrary(shaderLibrary); @@ -356,9 +358,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void }, .DepthStencil = { - .DepthStencil = applicationPayload->DepthBuffer, - .DepthClearValue = 0.0f, - .DepthLoadAction = ElemRenderPassLoadAction_Clear + .DepthStencil = applicationPayload->DepthBuffer } }); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 161d541a..020302e0 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -403,6 +403,18 @@ typedef enum ElemGraphicsResourceDescriptorUsage_Write = 0x01 } ElemGraphicsResourceDescriptorUsage; +typedef enum +{ + ElemGraphicsCompareFunction_Never = 0, + ElemGraphicsCompareFunction_Less = 1, + ElemGraphicsCompareFunction_Equal = 2, + ElemGraphicsCompareFunction_LessEqual = 3, + ElemGraphicsCompareFunction_Greater = 4, + ElemGraphicsCompareFunction_NotEqual = 5, + ElemGraphicsCompareFunction_GreaterEqual = 6, + ElemGraphicsCompareFunction_Always = 7 +} ElemGraphicsCompareFunction; + typedef enum { ElemGraphicsResourceBarrierSyncType_None, @@ -434,12 +446,12 @@ typedef enum */ typedef enum { + // Clears to a predefined value. + ElemRenderPassLoadAction_Clear = 0, // Discards the previous contents. - ElemRenderPassLoadAction_Discard = 0, + ElemRenderPassLoadAction_Discard = 1, // Loads the existing contents. - ElemRenderPassLoadAction_Load = 1, - // Clears to a predefined value. - ElemRenderPassLoadAction_Clear = 2 + ElemRenderPassLoadAction_Load = 2, } ElemRenderPassLoadAction; /** @@ -692,6 +704,9 @@ typedef struct // Supported texture formats for the pipeline state. ElemGraphicsFormatSpan RenderTargetFormats; ElemGraphicsFormat DepthStencilFormat; + + bool DepthWrite; + ElemGraphicsCompareFunction DepthCompareFunction; // Optional debug name for the pipeline state. const char* DebugName; } ElemGraphicsPipelineStateParameters; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 71183352..6a0f601b 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -131,6 +131,36 @@ void DirectX12FreeShaderLibrary(ElemShaderLibrary shaderLibrary) // TODO: Free data } +D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return D3D12_COMPARISON_FUNC_NEVER; + + case ElemGraphicsCompareFunction_Less: + return D3D12_COMPARISON_FUNC_LESS; + + case ElemGraphicsCompareFunction_Equal: + return D3D12_COMPARISON_FUNC_EQUAL; + + case ElemGraphicsCompareFunction_LessEqual: + return D3D12_COMPARISON_FUNC_LESS_EQUAL; + + case ElemGraphicsCompareFunction_Greater: + return D3D12_COMPARISON_FUNC_GREATER; + + case ElemGraphicsCompareFunction_NotEqual: + return D3D12_COMPARISON_FUNC_NOT_EQUAL; + + case ElemGraphicsCompareFunction_GreaterEqual: + return D3D12_COMPARISON_FUNC_GREATER_EQUAL; + + case ElemGraphicsCompareFunction_Always: + return D3D12_COMPARISON_FUNC_ALWAYS; + } +} + ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDevice, const ElemGraphicsPipelineStateParameters* parameters) { auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); @@ -142,7 +172,6 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev auto shaderLibraryData= GetDirectX12ShaderLibraryData(parameters->ShaderLibrary); SystemAssert(shaderLibraryData); - D3D12_RT_FORMAT_ARRAY renderTargets = {}; renderTargets.NumRenderTargets = 1; @@ -174,38 +203,19 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev rasterizerState.ForcedSampleCount = 0; rasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - // TODO: With Unit tests D3D12_DEPTH_STENCIL_DESC depthStencilState = {}; - // HACK: Temporary - /*if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) - { - depthStencilState.DepthEnable = true; - depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER; - }*/ - - /*if (renderPassDescriptor.DepthBufferOperation != GraphicsDepthBufferOperation::DepthNone) + if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) { depthStencilState.DepthEnable = true; - depthStencilState.StencilEnable = false; - if (renderPassDescriptor.DepthBufferOperation == GraphicsDepthBufferOperation::ClearWrite || - renderPassDescriptor.DepthBufferOperation == GraphicsDepthBufferOperation::Write) + if (parameters->DepthWrite) { depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; } - if (renderPassDescriptor.DepthBufferOperation == GraphicsDepthBufferOperation::CompareEqual) - { - depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_EQUAL; - } - - else - { - depthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_GREATER; - } - }*/ + depthStencilState.DepthFunc = ConvertToDirectX12CompareFunction(parameters->DepthCompareFunction); + } D3D12_BLEND_DESC blendState = {}; blendState.AlphaToCoverageEnable = false; diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Common/DirectXShaderCompiler.cpp index 50091b55..dfe370c9 100644 --- a/src/ElementalTools/Common/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Common/DirectXShaderCompiler.cpp @@ -136,7 +136,7 @@ ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn } parameters[parameterIndex++] = L"-HV"; - parameters[parameterIndex++] = L"2021"; + parameters[parameterIndex++] = L"202x"; parameters[parameterIndex++] = DXC_ARG_ALL_RESOURCES_BOUND; diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index a9a4a8fb..0477ae3d 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -45,7 +45,7 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); TestFreeGpuBuffer(readbackBuffer); @@ -96,7 +96,7 @@ UTEST(Rendering, RenderPassClearDepthBuffer) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)depthBuffer.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTextureFloat", 1, 1, 1, &resourceIdList); + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTextureFloat", 1, 1, 1, &resourceIdList); auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); TestFreeGpuBuffer(readbackBuffer); @@ -159,7 +159,7 @@ UTEST(Rendering, DispatchMesh) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); TestFreeGpuBuffer(readbackBuffer); diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index eccae5f0..f3e8dcb7 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -4,6 +4,10 @@ // TODO: Validate dispatch thread group count // TODO: Cannot push constant before binding pso +// TODO: PSO Multi render targets +// TODO: PSO Blend state +// TODO: PSO Cull order + UTEST(Shader, CompileComputePipelineState) { @@ -100,7 +104,18 @@ UTEST(Shader, DispatchCompute) } } -UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) +struct Shader_CompileGraphicsPipelineStateDepthCompare +{ + float ClearDepthValue; + ElemGraphicsCompareFunction CompareFunction; + float Color[3]; +}; + +UTEST_F_SETUP(Shader_CompileGraphicsPipelineStateDepthCompare) +{ +} + +UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -113,16 +128,18 @@ UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) ElemGraphicsPipelineStateParameters psoParameters = { .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 }, - .DepthStencilFormat = depthBuffer.Format + .DepthStencilFormat = depthBuffer.Format, + .DepthWrite = true, + .DepthCompareFunction = utest_fixture->CompareFunction }; - auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "ShaderTests.shader", "MeshShader", "PixelShader", &psoParameters); // Act ElemRenderPassRenderTarget renderPassRenderTarget = { .RenderTarget = renderTarget.Texture, - .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 0.0f, .Alpha = 1.0f }, .LoadAction = ElemRenderPassLoadAction_Clear }; @@ -136,14 +153,25 @@ UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) .DepthStencil = { .DepthStencil = depthBuffer.Texture, - .DepthClearValue = 0.0f, - .DepthLoadAction = ElemRenderPassLoadAction_Clear + .DepthClearValue = utest_fixture->ClearDepthValue } }; ElemBeginRenderPass(commandList, ¶meters); ElemBindPipelineState(commandList, meshShaderPipeline); + + float shaderParameters[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.25f }; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters, .Length = sizeof(float) * 8 }); + ElemDispatchMesh(commandList, 1, 1, 1); + + float shaderParameters2[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f }; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters2, .Length = sizeof(float) * 8 }); + ElemDispatchMesh(commandList, 1, 1, 1); + + float shaderParameters3[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.75f }; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters3, .Length = sizeof(float) * 8 }); ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); // Assert @@ -153,7 +181,7 @@ UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); TestFreeGpuBuffer(readbackBuffer); @@ -169,85 +197,73 @@ UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreater) for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) { - ASSERT_EQ_MSG(floatData[i], 0.0f, "Red channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 1], 0.0f, "Green channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 2], 1.0f, "Blue channel data is invalid."); + ASSERT_EQ_MSG(floatData[i], utest_fixture->Color[0], "Red channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 1], utest_fixture->Color[1], "Green channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 2], utest_fixture->Color[2], "Blue channel data is invalid."); ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); } } -UTEST(Shader, CompileGraphicsPipelineStateDepthCompareGreaterOrEquals) +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Never) { - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); - - ElemGraphicsPipelineStateParameters psoParameters = - { - .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 }, - .DepthStencilFormat = depthBuffer.Format - }; - - auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); - - // Act - ElemRenderPassRenderTarget renderPassRenderTarget = - { - .RenderTarget = renderTarget.Texture, - .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, - .LoadAction = ElemRenderPassLoadAction_Clear - }; - - ElemBeginRenderPassParameters parameters = - { - .RenderTargets = - { - .Items = &renderPassRenderTarget, - .Length = 1 - }, - .DepthStencil = - { - .DepthStencil = depthBuffer.Texture, - .DepthClearValue = 0.0f, - .DepthLoadAction = ElemRenderPassLoadAction_Clear - } - }; + utest_fixture->ClearDepthValue = 0.5f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_Never; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 0.0f; +} - ElemBeginRenderPass(commandList, ¶meters); - ElemBindPipelineState(commandList, meshShaderPipeline); - ElemDispatchMesh(commandList, 1, 1, 1); - ElemEndRenderPass(commandList); - - // Assert - ElemCommitCommandList(commandList); - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - ElemWaitForFenceOnCpu(fence); +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Less) +{ + utest_fixture->ClearDepthValue = 1.0f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_Less; + utest_fixture->Color[0] = 1.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 0.0f; +} - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); - uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "RenderingTests.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, LessEqual) +{ + utest_fixture->ClearDepthValue = 0.25f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_LessEqual; + utest_fixture->Color[0] = 1.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 0.0f; +} - TestFreeGpuBuffer(readbackBuffer); - TestFreeGpuTexture(renderTarget); - TestFreeGpuTexture(depthBuffer); - ElemFreePipelineState(meshShaderPipeline); - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsDevice(graphicsDevice); +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Greater) +{ + utest_fixture->ClearDepthValue = 0.0f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_Greater; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 1.0f; +} - ASSERT_LOG_NOERROR(); +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, GreaterEqual) +{ + utest_fixture->ClearDepthValue = 0.75f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_GreaterEqual; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 1.0f; +} - auto floatData = (float*)bufferData.Items; +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Equal) +{ + utest_fixture->ClearDepthValue = 0.5f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_Equal; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 1.0f; + utest_fixture->Color[2] = 0.0f; +} - for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) - { - ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 1], 1.0f, "Green channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 2], 0.0f, "Blue channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); - } +UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Always) +{ + utest_fixture->ClearDepthValue = 1.0f; + utest_fixture->CompareFunction = ElemGraphicsCompareFunction_Always; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 1.0f; } +// TODO: Check depth stencil format if comparaison function set diff --git a/tests/GraphicsTests/Shaders/Assert.hlsl b/tests/GraphicsTests/Shaders/Assert.hlsl new file mode 100644 index 00000000..47323ba6 --- /dev/null +++ b/tests/GraphicsTests/Shaders/Assert.hlsl @@ -0,0 +1,75 @@ +// TODO: Move copy texture to assert.hlsl and rename +struct Parameters +{ + uint SourceBufferIndex; + uint DestinationBufferIndex; +}; + +[[vk::push_constant]] +Parameters parameters : register(b0); + +[shader("compute")] +[numthreads(16, 16, 1)] +void CopyTexture(uint2 threadId: SV_DispatchThreadID) +{ + Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; + RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; + + destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); +} + +[shader("compute")] +[numthreads(16, 16, 1)] +void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) +{ + Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; + RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; + + destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); +} + +struct VertexOutput +{ + float4 Position: SV_Position; +}; + +static float3 rectangleVertices[] = +{ + float3(-1.0, 1.0, 0.0), + float3(1.0, 1.0, 0.0), + float3(-1.0, -1.0, 0.0), + float3(1.0, -1.0, 0.0) +}; + +static uint3 rectanglePrimitives[] = +{ + uint3(0, 1, 2), + uint3(2, 1, 3) +}; + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(4, 1, 1)] +void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) +{ + const uint meshVertexCount = 4; + const uint meshPrimitiveCount = 2; + + SetMeshOutputCounts(meshVertexCount, meshPrimitiveCount); + + if (groupThreadId < meshVertexCount) + { + vertices[groupThreadId].Position = float4(rectangleVertices[groupThreadId], 1); + } + + if (groupThreadId < meshPrimitiveCount) + { + indices[groupThreadId] = rectanglePrimitives[groupThreadId]; + } +} + +[shader("pixel")] +float4 PixelShader(const VertexOutput input) : SV_Target0 +{ + return float4(1.0, 1.0, 0.0, 1.0); +} diff --git a/tests/GraphicsTests/Shaders/RenderingTests.hlsl b/tests/GraphicsTests/Shaders/RenderingTests.hlsl index 47323ba6..6080ffe9 100644 --- a/tests/GraphicsTests/Shaders/RenderingTests.hlsl +++ b/tests/GraphicsTests/Shaders/RenderingTests.hlsl @@ -1,33 +1,3 @@ -// TODO: Move copy texture to assert.hlsl and rename -struct Parameters -{ - uint SourceBufferIndex; - uint DestinationBufferIndex; -}; - -[[vk::push_constant]] -Parameters parameters : register(b0); - -[shader("compute")] -[numthreads(16, 16, 1)] -void CopyTexture(uint2 threadId: SV_DispatchThreadID) -{ - Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; - RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; - - destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); -} - -[shader("compute")] -[numthreads(16, 16, 1)] -void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) -{ - Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; - RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; - - destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); -} - struct VertexOutput { float4 Position: SV_Position; diff --git a/tests/GraphicsTests/Shaders/ShaderTests.hlsl b/tests/GraphicsTests/Shaders/ShaderTests.hlsl index 31785334..0b643d3d 100644 --- a/tests/GraphicsTests/Shaders/ShaderTests.hlsl +++ b/tests/GraphicsTests/Shaders/ShaderTests.hlsl @@ -1,6 +1,7 @@ struct TestParameters { uint TestDescriptor; + float4 ColorAndDepth; }; [[vk::push_constant]] @@ -13,3 +14,49 @@ void TestCompute(uint2 threadId: SV_DispatchThreadID) RWStructuredBuffer testBuffer = ResourceDescriptorHeap[parameters.TestDescriptor]; testBuffer[threadId.x] = threadId.x; } + +struct VertexOutput +{ + float4 Position: SV_Position; +}; + +static float3 rectangleVertices[] = +{ + float3(-1.0, 1.0, 0.0), + float3(1.0, 1.0, 0.0), + float3(-1.0, -1.0, 0.0), + float3(1.0, -1.0, 0.0) +}; + +static uint3 rectanglePrimitives[] = +{ + uint3(0, 1, 2), + uint3(2, 1, 3) +}; + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(4, 1, 1)] +void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) +{ + const uint meshVertexCount = 4; + const uint meshPrimitiveCount = 2; + + SetMeshOutputCounts(meshVertexCount, meshPrimitiveCount); + + if (groupThreadId < meshVertexCount) + { + vertices[groupThreadId].Position = float4(rectangleVertices[groupThreadId].xy, parameters.ColorAndDepth.w, 1); + } + + if (groupThreadId < meshPrimitiveCount) + { + indices[groupThreadId] = rectanglePrimitives[groupThreadId]; + } +} + +[shader("pixel")] +float4 PixelShader(const VertexOutput input) : SV_Target0 +{ + return float4(parameters.ColorAndDepth.rgb, 1.0); +} From baa0d412dce06d99b0e0859eaa3c18b079b71289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 20 Aug 2024 16:42:23 +0200 Subject: [PATCH 10/93] Add blending PSO tests --- samples/Elemental/02-HelloTriangle/main.c | 2 +- samples/Elemental/03-HelloInputs/main.c | 2 +- samples/Elemental/04-HelloCompute/main.c | 2 +- .../05-HelloMesh/Data/RenderMesh.hlsl | 4 +- samples/Elemental/05-HelloMesh/main.c | 11 +- samples/XX-MinimalTriangle/main.c | 2 +- .../Common/Graphics/Vulkan/VulkanShader.cpp | 2 +- src/Elemental/Elemental.h | 58 +- .../Microsoft/Graphics/DirectX12Shader.cpp | 216 ++++--- .../Microsoft/Graphics/DirectX12Shader.h | 4 +- tests/GraphicsTests/GraphicsTests.cpp | 3 +- tests/GraphicsTests/RenderingTests.cpp | 5 +- tests/GraphicsTests/ResourceBarrierTests.cpp | 3 +- tests/GraphicsTests/ShaderTests.cpp | 546 +++++++++++++++++- tests/GraphicsTests/Shaders/ShaderTests.hlsl | 8 +- 15 files changed, 752 insertions(+), 116 deletions(-) diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c index 225f3c3a..501933d0 100644 --- a/samples/Elemental/02-HelloTriangle/main.c +++ b/samples/Elemental/02-HelloTriangle/main.c @@ -41,7 +41,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 55c3230b..95933dd2 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -182,7 +182,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, }); applicationPayload->ShaderParameters.RotationQuaternion = (SampleVector4){ .X = 0, .Y = 0, .Z = 0, .W = 1 }; diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index dd7706f3..c931de05 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -118,7 +118,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl index f1716760..3ae7fc3f 100644 --- a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl @@ -135,13 +135,13 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 { if (parameters.TriangleColor == 0) { - return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1); + return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); } else { uint hashResult = hash(input.MeshletIndex); float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; - return float4(meshletColor, 1); + return float4(meshletColor, 1.0); } } diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index 11fcd462..67c61e45 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -162,10 +162,12 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 }, - .DepthStencilFormat = ElemGraphicsFormat_D32_FLOAT, - .DepthWrite = true, - .DepthCompareFunction = ElemGraphicsCompareFunction_Greater + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, + .DepthStencil = + { + .Format = ElemGraphicsFormat_D32_FLOAT, + .DepthCompareFunction = ElemGraphicsCompareFunction_Greater + } }); ElemFreeShaderLibrary(shaderLibrary); @@ -212,6 +214,7 @@ void ResetTouchParameters(GameState* gameState) gameState->PreviousTouchAngle = 0.0f; } +// TODO: Extract model view gamestate update void UpdateGameState(GameState* gameState, SampleModelViewerInputActions* inputActions, float deltaTimeInSeconds) { gameState->RotationDelta = V3Zero; diff --git a/samples/XX-MinimalTriangle/main.c b/samples/XX-MinimalTriangle/main.c index c5f66495..085976b7 100644 --- a/samples/XX-MinimalTriangle/main.c +++ b/samples/XX-MinimalTriangle/main.c @@ -72,7 +72,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargetFormats = { .Items = (ElemGraphicsFormat[]) { swapChainInfo.Format }, .Length = 1 } + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 } }); ElemFreeShaderLibrary(shaderLibrary); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 46f6cf89..35f43d28 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -238,7 +238,7 @@ ElemPipelineState VulkanCompileGraphicsPipelineState(ElemGraphicsDevice graphics dynamicState.pDynamicStates = dynamicStates; createInfo.pDynamicState = &dynamicState; - VkFormat formats[] = { ConvertToVulkanTextureFormat(parameters->RenderTargetFormats.Items[0]) }; // TODO: Fill Correct Back Buffer Format + VkFormat formats[] = { ConvertToVulkanTextureFormat(parameters->RenderTargets.Items[0].Format) }; // TODO: Fill Correct Back Buffer Format VkPipelineRenderingCreateInfo renderingCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; renderingCreateInfo.colorAttachmentCount = 1; // TODO: Change that diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 020302e0..a2f8c7a9 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -403,6 +403,30 @@ typedef enum ElemGraphicsResourceDescriptorUsage_Write = 0x01 } ElemGraphicsResourceDescriptorUsage; +typedef enum +{ + ElemGraphicsBlendOperation_Add = 0, + ElemGraphicsBlendOperation_Substract = 1, + ElemGraphicsBlendOperation_ReverseSubstract = 2, + ElemGraphicsBlendOperation_Min = 3, + ElemGraphicsBlendOperation_Max = 4, +} ElemGraphicsBlendOperation; + +typedef enum +{ + ElemGraphicsBlendFactor_Zero = 0, + ElemGraphicsBlendFactor_One = 1, + ElemGraphicsBlendFactor_SourceColor = 2, + ElemGraphicsBlendFactor_InverseSourceColor = 3, + ElemGraphicsBlendFactor_SourceAlpha = 4, + ElemGraphicsBlendFactor_InverseSourceAlpha = 5, + ElemGraphicsBlendFactor_DestinationColor = 6, + ElemGraphicsBlendFactor_InverseDestinationColor = 7, + ElemGraphicsBlendFactor_DestinationAlpha = 8, + ElemGraphicsBlendFactor_InverseDestinationAlpha = 9, + ElemGraphicsBlendFactor_SourceAlphaSaturated = 10, +} ElemGraphicsBlendFactor; + typedef enum { ElemGraphicsCompareFunction_Never = 0, @@ -679,16 +703,34 @@ typedef struct ElemFenceSpan FencesToWait; } ElemFreeGraphicsResourceDescriptorOptions; +typedef struct +{ + ElemGraphicsFormat Format; + ElemGraphicsBlendOperation BlendOperation; + ElemGraphicsBlendFactor SourceBlendFactor; + ElemGraphicsBlendFactor DestinationBlendFactor; + ElemGraphicsBlendOperation BlendOperationAlpha; + ElemGraphicsBlendFactor SourceBlendFactorAlpha; + ElemGraphicsBlendFactor DestinationBlendFactorAlpha; +} ElemGraphicsPipelineStateRenderTarget; + +typedef struct +{ + ElemGraphicsFormat Format; + bool DepthDisableWrite; + ElemGraphicsCompareFunction DepthCompareFunction; +} ElemGraphicsPipelineStateDepthStencil; + /** * Represents a collection of texture formats. */ typedef struct { - // Pointer to an array of ElemGraphicsFormat. - ElemGraphicsFormat* Items; + // Pointer to an array of ElemGraphicsPipelineStateRenderTarget. + ElemGraphicsPipelineStateRenderTarget* Items; // Number of items in the array. uint32_t Length; -} ElemGraphicsFormatSpan; +} ElemGraphicsPipelineStateRenderTargetSpan; /** * Parameters for creating a graphics pipeline state. @@ -701,12 +743,8 @@ typedef struct const char* MeshShaderFunction; // Function name of the pixel shader in the shader library. const char* PixelShaderFunction; - // Supported texture formats for the pipeline state. - ElemGraphicsFormatSpan RenderTargetFormats; - ElemGraphicsFormat DepthStencilFormat; - - bool DepthWrite; - ElemGraphicsCompareFunction DepthCompareFunction; + ElemGraphicsPipelineStateRenderTargetSpan RenderTargets; + ElemGraphicsPipelineStateDepthStencil DepthStencil; // Optional debug name for the pipeline state. const char* DebugName; } ElemGraphicsPipelineStateParameters; @@ -785,7 +823,6 @@ typedef struct typedef struct { ElemGraphicsResource RenderTarget; - // Color to clear the render target with if the load action is clear. ElemColor ClearColor; // Action to take when loading data into the render target at the beginning of a render pass. @@ -797,7 +834,6 @@ typedef struct typedef struct { ElemGraphicsResource DepthStencil; - // TODO: Specify read or write mode ? (write by default) float DepthClearValue; ElemRenderPassLoadAction DepthLoadAction; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 6a0f601b..b72da555 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -94,41 +94,74 @@ D3D12_SHADER_BYTECODE GetDirectX12ShaderFunctionByteCode(DirectX12ShaderLibraryD return result; } -ElemShaderLibrary DirectX12CreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) +bool IsDirectX12BlendEnabled(ElemGraphicsPipelineStateRenderTarget renderTargetParameters) { - InitDirectX12ShaderMemory(); - - D3D12_SHADER_BYTECODE directX12LibraryData = {}; - ReadOnlySpan graphicsShaderData = {}; + return !(renderTargetParameters.BlendOperation == ElemGraphicsBlendOperation_Add && + renderTargetParameters.SourceBlendFactor == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.DestinationBlendFactor == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.BlendOperationAlpha == ElemGraphicsBlendOperation_Add && + renderTargetParameters.SourceBlendFactorAlpha == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.DestinationBlendFactorAlpha == ElemGraphicsBlendFactor_Zero); +} - if (CheckDirectX12ShaderDataHeader(shaderLibraryData, "DXBC")) +D3D12_BLEND_OP ConvertToDirectX12BlendOperation(ElemGraphicsBlendOperation blendOperation) +{ + switch (blendOperation) { - auto dest = SystemPushArray(DirectX12MemoryArena, shaderLibraryData.Length); - SystemCopyBuffer(dest, ReadOnlySpan(shaderLibraryData.Items, shaderLibraryData.Length)); + case ElemGraphicsBlendOperation_Add: + return D3D12_BLEND_OP_ADD; - directX12LibraryData = - { - .pShaderBytecode = dest.Pointer, - .BytecodeLength = dest.Length - }; - } - else - { - auto dataSpan = Span(shaderLibraryData.Items, shaderLibraryData.Length); - graphicsShaderData = ReadShaders(DirectX12MemoryArena, dataSpan); - } + case ElemGraphicsBlendOperation_Substract: + return D3D12_BLEND_OP_ADD; - auto handle = SystemAddDataPoolItem(directX12ShaderLibraryPool, { - .ShaderLibraryData = directX12LibraryData, - .GraphicsShaders = graphicsShaderData - }); + case ElemGraphicsBlendOperation_ReverseSubstract: + return D3D12_BLEND_OP_ADD; - return handle; + case ElemGraphicsBlendOperation_Min: + return D3D12_BLEND_OP_ADD; + + case ElemGraphicsBlendOperation_Max: + return D3D12_BLEND_OP_ADD; + } } -void DirectX12FreeShaderLibrary(ElemShaderLibrary shaderLibrary) +D3D12_BLEND ConvertToDirectX12Blend(ElemGraphicsBlendFactor blendFactor) { - // TODO: Free data + switch (blendFactor) + { + case ElemGraphicsBlendFactor_Zero: + return D3D12_BLEND_ZERO; + + case ElemGraphicsBlendFactor_One: + return D3D12_BLEND_ONE; + + case ElemGraphicsBlendFactor_SourceColor: + return D3D12_BLEND_SRC_COLOR; + + case ElemGraphicsBlendFactor_InverseSourceColor: + return D3D12_BLEND_INV_SRC_COLOR; + + case ElemGraphicsBlendFactor_SourceAlpha: + return D3D12_BLEND_SRC_ALPHA; + + case ElemGraphicsBlendFactor_InverseSourceAlpha: + return D3D12_BLEND_INV_SRC_ALPHA; + + case ElemGraphicsBlendFactor_DestinationColor: + return D3D12_BLEND_DEST_COLOR; + + case ElemGraphicsBlendFactor_InverseDestinationColor: + return D3D12_BLEND_INV_DEST_COLOR; + + case ElemGraphicsBlendFactor_DestinationAlpha: + return D3D12_BLEND_DEST_ALPHA; + + case ElemGraphicsBlendFactor_InverseDestinationAlpha: + return D3D12_BLEND_INV_DEST_ALPHA; + + case ElemGraphicsBlendFactor_SourceAlphaSaturated: + return D3D12_BLEND_SRC_ALPHA_SAT; + } } D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunction compareFunction) @@ -161,6 +194,43 @@ D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunct } } +ElemShaderLibrary DirectX12CreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) +{ + InitDirectX12ShaderMemory(); + + D3D12_SHADER_BYTECODE directX12LibraryData = {}; + ReadOnlySpan graphicsShaderData = {}; + + if (CheckDirectX12ShaderDataHeader(shaderLibraryData, "DXBC")) + { + auto dest = SystemPushArray(DirectX12MemoryArena, shaderLibraryData.Length); + SystemCopyBuffer(dest, ReadOnlySpan(shaderLibraryData.Items, shaderLibraryData.Length)); + + directX12LibraryData = + { + .pShaderBytecode = dest.Pointer, + .BytecodeLength = dest.Length + }; + } + else + { + auto dataSpan = Span(shaderLibraryData.Items, shaderLibraryData.Length); + graphicsShaderData = ReadShaders(DirectX12MemoryArena, dataSpan); + } + + auto handle = SystemAddDataPoolItem(directX12ShaderLibraryPool, { + .ShaderLibraryData = directX12LibraryData, + .GraphicsShaders = graphicsShaderData + }); + + return handle; +} + +void DirectX12FreeShaderLibrary(ElemShaderLibrary shaderLibrary) +{ + // TODO: Free data +} + ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDevice, const ElemGraphicsPipelineStateParameters* parameters) { auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); @@ -172,23 +242,60 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev auto shaderLibraryData= GetDirectX12ShaderLibraryData(parameters->ShaderLibrary); SystemAssert(shaderLibraryData); - D3D12_RT_FORMAT_ARRAY renderTargets = {}; + // TODO: Extract methods (this will be easier to switch to new PipelineState later) + + // TODO: Render targets + D3D12_RT_FORMAT_ARRAY renderTargets = + { + .NumRenderTargets = parameters->RenderTargets.Length + }; + + D3D12_BLEND_DESC blendState = + { + .IndependentBlendEnable = false, // TODO: I think that one is needed + }; + + for (uint32_t i = 0; i < parameters->RenderTargets.Length; i++) + { + auto renderTargetParameters = parameters->RenderTargets.Items[i]; + + renderTargets.RTFormats[i] = ConvertToDirectX12TextureFormat(renderTargetParameters.Format); + + blendState.RenderTarget[i] = + { + .BlendEnable = IsDirectX12BlendEnabled(renderTargetParameters), + .SrcBlend = ConvertToDirectX12Blend(renderTargetParameters.SourceBlendFactor), + .DestBlend = ConvertToDirectX12Blend(renderTargetParameters.DestinationBlendFactor), + .BlendOp = ConvertToDirectX12BlendOperation(renderTargetParameters.BlendOperation), + .SrcBlendAlpha = ConvertToDirectX12Blend(renderTargetParameters.SourceBlendFactorAlpha), + .DestBlendAlpha = ConvertToDirectX12Blend(renderTargetParameters.DestinationBlendFactorAlpha), + .BlendOpAlpha = ConvertToDirectX12BlendOperation(renderTargetParameters.BlendOperationAlpha), + .RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL + }; + } - renderTargets.NumRenderTargets = 1; - renderTargets.RTFormats[0] = ConvertToDirectX12TextureFormat(parameters->RenderTargetFormats.Items[0]); // TODO: Fill Correct Back Buffer Format - DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN; + D3D12_DEPTH_STENCIL_DESC2 depthStencilState = {}; - if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) + if (CheckDirectX12DepthStencilFormat(parameters->DepthStencil.Format)) { - depthFormat = ConvertToDirectX12TextureFormat(parameters->DepthStencilFormat); + depthFormat = ConvertToDirectX12TextureFormat(parameters->DepthStencil.Format); + depthStencilState.DepthEnable = true; + + if (!parameters->DepthStencil.DepthDisableWrite) + { + depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + } + + depthStencilState.DepthFunc = ConvertToDirectX12CompareFunction(parameters->DepthStencil.DepthCompareFunction); } DXGI_SAMPLE_DESC sampleDesc = {}; sampleDesc.Count = 1; sampleDesc.Quality = 0; - D3D12_RASTERIZER_DESC rasterizerState = {}; + D3D12_RASTERIZER_DESC2 rasterizerState = {}; + //rasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME; rasterizerState.FillMode = D3D12_FILL_MODE_SOLID; rasterizerState.CullMode = D3D12_CULL_MODE_NONE; // D3D12_CULL_MODE_BACK; rasterizerState.FrontCounterClockwise = false; @@ -196,53 +303,12 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev rasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; rasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; rasterizerState.DepthClipEnable = true; - rasterizerState.MultisampleEnable = false; - rasterizerState.AntialiasedLineEnable = false; + rasterizerState.LineRasterizationMode = D3D12_LINE_RASTERIZATION_MODE_ALIASED; // TODO: Check those 2 rasterizerState.ForcedSampleCount = 0; rasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - D3D12_DEPTH_STENCIL_DESC depthStencilState = {}; - - if (CheckDirectX12DepthStencilFormat(parameters->DepthStencilFormat)) - { - depthStencilState.DepthEnable = true; - - if (parameters->DepthWrite) - { - depthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - } - - depthStencilState.DepthFunc = ConvertToDirectX12CompareFunction(parameters->DepthCompareFunction); - } - - D3D12_BLEND_DESC blendState = {}; - blendState.AlphaToCoverageEnable = false; - blendState.IndependentBlendEnable = false; -/* - if (renderPassDescriptor.RenderTarget1BlendOperation.HasValue) - { - auto blendOperation = renderPassDescriptor.RenderTarget1BlendOperation.Value; - blendState.RenderTarget[0] = InitBlendState(blendOperation); - } - - else - {*/ - blendState.RenderTarget[0] = { - false, - false, - D3D12_BLEND_ONE, - D3D12_BLEND_ZERO, - D3D12_BLEND_OP_ADD, - D3D12_BLEND_ONE, - D3D12_BLEND_ZERO, - D3D12_BLEND_OP_ADD, - D3D12_LOGIC_OP_NOOP, - D3D12_COLOR_WRITE_ENABLE_ALL, - }; // InitBlendState(GraphicsBlendOperation::None); - //} - D3D12_PIPELINE_STATE_STREAM_DESC psoStream = {}; GraphicsPso psoDesc = {}; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.h b/src/Elemental/Microsoft/Graphics/DirectX12Shader.h index c44b0d3f..46ce1dca 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.h @@ -64,8 +64,8 @@ struct GraphicsPso PsoSubObject(PS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS, D3D12_SHADER_BYTECODE); PsoSubObject(RenderTargets, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS, D3D12_RT_FORMAT_ARRAY); PsoSubObject(SampleDesc, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC, DXGI_SAMPLE_DESC); - PsoSubObject(RasterizerState, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER, D3D12_RASTERIZER_DESC); + PsoSubObject(RasterizerState, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER2, D3D12_RASTERIZER_DESC2); PsoSubObject(DepthStencilFormat, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT, DXGI_FORMAT); - PsoSubObject(DepthStencilState, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL, D3D12_DEPTH_STENCIL_DESC); + PsoSubObject(DepthStencilState, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL2, D3D12_DEPTH_STENCIL_DESC2); PsoSubObject(BlendState, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, D3D12_BLEND_DESC); }; diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index 1422654b..78525987 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -52,7 +52,6 @@ void GetFullPath(char* destination, const char* path) CopyString(pointer, pointer - destination, folderPrefix, strlen(folderPrefix)); pointer = pointer + strlen(folderPrefix); - CopyString(pointer, pointer - destination, path, strlen(path)); } @@ -163,7 +162,7 @@ const char* ReplaceShaderFilenameForVulkan(const char* original) } // Copy the base part - strncpy(newString, original, baseLength); + CopyString(newString, newLength, original, baseLength); newString[baseLength] = '\0'; // Null-terminate the base part // Append the "_vulkan" string diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 0477ae3d..5fdef375 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -122,10 +122,11 @@ UTEST(Rendering, DispatchMesh) auto commandList = ElemGetCommandList(commandQueue, nullptr); auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = { - .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 } + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } }; auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); diff --git a/tests/GraphicsTests/ResourceBarrierTests.cpp b/tests/GraphicsTests/ResourceBarrierTests.cpp index ae60c8cc..e08ef295 100644 --- a/tests/GraphicsTests/ResourceBarrierTests.cpp +++ b/tests/GraphicsTests/ResourceBarrierTests.cpp @@ -315,9 +315,10 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) auto writeTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestWriteTextureData"); auto readTextureDataPipelineState = TestOpenComputeShader(graphicsDevice, "ResourceBarrierTests.shader", "TestReadTextureData"); + ElemGraphicsPipelineStateRenderTarget psoRenderTarget { .Format = gpuTexture.Format }; ElemGraphicsPipelineStateParameters psoParameters = { - .RenderTargetFormats = { .Items = &gpuTexture.Format, .Length = 1 } + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } }; auto meshShaderPipelineState = TestOpenMeshShader(graphicsDevice, "ResourceBarrierTests.shader", "TestMeshShader", "TestPixelShader", &psoParameters); diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index f3e8dcb7..dde229dc 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -4,9 +4,12 @@ // TODO: Validate dispatch thread group count // TODO: Cannot push constant before binding pso -// TODO: PSO Multi render targets +// TODO: PSO Multi render targets (Test Blend States too) // TODO: PSO Blend state +// TODO: PSO FillMode // TODO: PSO Cull order +// TODO: PSO Depth Bias? +// TODO: Check depth stencil format if comparaison function set UTEST(Shader, CompileComputePipelineState) @@ -125,12 +128,15 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + ElemGraphicsPipelineStateRenderTarget psoRenderTarget { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = { - .RenderTargetFormats = { .Items = &renderTarget.Format, .Length = 1 }, - .DepthStencilFormat = depthBuffer.Format, - .DepthWrite = true, - .DepthCompareFunction = utest_fixture->CompareFunction + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 }, + .DepthStencil = + { + .Format = depthBuffer.Format, + .DepthCompareFunction = utest_fixture->CompareFunction + } }; auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "ShaderTests.shader", "MeshShader", "PixelShader", &psoParameters); @@ -160,15 +166,15 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) ElemBeginRenderPass(commandList, ¶meters); ElemBindPipelineState(commandList, meshShaderPipeline); - float shaderParameters[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.25f }; + float shaderParameters[] = { 0.0f, 0.25f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f }; ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters, .Length = sizeof(float) * 8 }); ElemDispatchMesh(commandList, 1, 1, 1); - float shaderParameters2[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.5f }; + float shaderParameters2[] = { 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f }; ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters2, .Length = sizeof(float) * 8 }); ElemDispatchMesh(commandList, 1, 1, 1); - float shaderParameters3[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.75f }; + float shaderParameters3[] = { 0.0f, 0.75f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f }; ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters3, .Length = sizeof(float) * 8 }); ElemDispatchMesh(commandList, 1, 1, 1); @@ -266,4 +272,526 @@ UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Always) utest_fixture->Color[1] = 0.0f; utest_fixture->Color[2] = 1.0f; } -// TODO: Check depth stencil format if comparaison function set + +struct Shader_CompileGraphicsPipelineStateBlendState +{ + ElemGraphicsBlendOperation BlendOperation; + ElemGraphicsBlendFactor SourceBlendFactor; + ElemGraphicsBlendFactor DestinationBlendFactor; + ElemGraphicsBlendOperation BlendOperationAlpha; + ElemGraphicsBlendFactor SourceBlendFactorAlpha; + ElemGraphicsBlendFactor DestinationBlendFactorAlpha; + float DestinationColor[4]; + float SourceColor[4]; + float ResultColor[4]; +}; + +UTEST_F_SETUP(Shader_CompileGraphicsPipelineStateBlendState) +{ +} + +UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateBlendState) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = + { + .Format = renderTarget.Format, + .BlendOperation = utest_fixture->BlendOperation, + .SourceBlendFactor = utest_fixture->SourceBlendFactor, + .DestinationBlendFactor = utest_fixture->DestinationBlendFactor, + .BlendOperationAlpha = utest_fixture->BlendOperationAlpha, + .SourceBlendFactorAlpha = utest_fixture->SourceBlendFactorAlpha, + .DestinationBlendFactorAlpha = utest_fixture->DestinationBlendFactorAlpha + }; + + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 }, + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "ShaderTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = + { + .Red = utest_fixture->DestinationColor[0], + .Green = utest_fixture->DestinationColor[1], + .Blue = utest_fixture->DestinationColor[2], + .Alpha = utest_fixture->DestinationColor[3] + }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + + float shaderParameters[] = { 0.0f, 0.0f, 0.0f, 0.0f, utest_fixture->SourceColor[0], utest_fixture->SourceColor[1], utest_fixture->SourceColor[2], utest_fixture->SourceColor[3] }; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters, .Length = sizeof(float) * 8 }); + ElemDispatchMesh(commandList, 1, 1, 1); + + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto floatData = (float*)bufferData.Items; + + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) + { + ASSERT_EQ_MSG(floatData[i], utest_fixture->ResultColor[0], "Red channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 1], utest_fixture->ResultColor[1], "Green channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 2], utest_fixture->ResultColor[2], "Blue channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 3], utest_fixture->ResultColor[3], "Alpha channel data is invalid."); + } +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Defaults) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 0.5f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 1.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_Zero_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 1.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_One_Zero) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 0.5f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 1.5f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AddAlpha_Zero_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 0.5f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.5f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AddAlpha_One_Zero) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 0.5f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 1.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AddAlpha_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 0.5f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 1.5f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_SourceColor_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_SourceColor; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 1.25f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_InverseSourceColor_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_InverseSourceColor; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.25f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 1.1875f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_SourceAlpha_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlpha; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.25f; + utest_fixture->SourceColor[2] = 0.8; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.5f; + utest_fixture->ResultColor[1] = 1.125f; + utest_fixture->ResultColor[2] = 0.4f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_InverseSourceAlpha_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.8; + utest_fixture->SourceColor[3] = 0.75f; + + utest_fixture->ResultColor[0] = 0.25f; + utest_fixture->ResultColor[1] = 1.125f; + utest_fixture->ResultColor[2] = 0.2f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_DestinationColor_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_DestinationColor; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.5f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 1.5f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_InverseDestinationColor_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_InverseDestinationColor; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.5f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.25f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 1.0f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_DestinationAlpha_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_DestinationAlpha; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.5f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.25f; + utest_fixture->SourceColor[2] = 0.8; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 1.5f; + utest_fixture->ResultColor[1] = 1.25; + utest_fixture->ResultColor[2] = 0.8f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_InverseDestinationAlpha_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_InverseDestinationAlpha; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.5f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.8; + utest_fixture->SourceColor[3] = 0.75f; + + utest_fixture->ResultColor[0] = 0.5f; + utest_fixture->ResultColor[1] = 1.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_SourceAlphaSaturated_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlphaSaturated; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 0.0f; + utest_fixture->DestinationColor[3] = 0.75f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 0.25f; + utest_fixture->SourceColor[2] = 0.8; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.25f; + utest_fixture->ResultColor[1] = 1.0625f; + utest_fixture->ResultColor[2] = 0.2f; + utest_fixture->ResultColor[3] = 0.0f; +} + +// TODO: Other blend operators diff --git a/tests/GraphicsTests/Shaders/ShaderTests.hlsl b/tests/GraphicsTests/Shaders/ShaderTests.hlsl index 0b643d3d..8f01a2dd 100644 --- a/tests/GraphicsTests/Shaders/ShaderTests.hlsl +++ b/tests/GraphicsTests/Shaders/ShaderTests.hlsl @@ -1,7 +1,9 @@ struct TestParameters { uint TestDescriptor; - float4 ColorAndDepth; + float Depth; + uint2 Reserved; + float4 Color; }; [[vk::push_constant]] @@ -46,7 +48,7 @@ void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOut if (groupThreadId < meshVertexCount) { - vertices[groupThreadId].Position = float4(rectangleVertices[groupThreadId].xy, parameters.ColorAndDepth.w, 1); + vertices[groupThreadId].Position = float4(rectangleVertices[groupThreadId].xy, parameters.Depth, 1); } if (groupThreadId < meshPrimitiveCount) @@ -58,5 +60,5 @@ void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOut [shader("pixel")] float4 PixelShader(const VertexOutput input) : SV_Target0 { - return float4(parameters.ColorAndDepth.rgb, 1.0); + return float4(parameters.Color.rgba); } From 158e9a89d3b23db83d4fca158f354a2656f53ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 21 Aug 2024 16:13:02 +0200 Subject: [PATCH 11/93] Add PSO CullMode and FillMode tests --- samples/Elemental/05-HelloMesh/main.c | 1 - src/Elemental/Elemental.h | 15 + .../Microsoft/Graphics/DirectX12Shader.cpp | 40 +- tests/GraphicsTests/RenderingTests.cpp | 2 +- tests/GraphicsTests/ShaderTests.cpp | 371 +++++++++++++++++- 5 files changed, 414 insertions(+), 15 deletions(-) diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index 67c61e45..a32c70ae 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -355,7 +355,6 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void { .RenderTarget = updateParameters->BackBufferRenderTarget, .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, - .LoadAction = ElemRenderPassLoadAction_Clear }}, .Length = 1 }, diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index a2f8c7a9..757a4ffe 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -403,6 +403,19 @@ typedef enum ElemGraphicsResourceDescriptorUsage_Write = 0x01 } ElemGraphicsResourceDescriptorUsage; +typedef enum +{ + ElemGraphicsFillMode_Solid = 0, + ElemGraphicsFillMode_Wireframe = 1 +} ElemGraphicsFillMode; + +typedef enum +{ + ElemGraphicsCullMode_BackFace = 0, + ElemGraphicsCullMode_FrontFace = 1, + ElemGraphicsCullMode_None = 2 +} ElemGraphicsCullMode; + typedef enum { ElemGraphicsBlendOperation_Add = 0, @@ -745,6 +758,8 @@ typedef struct const char* PixelShaderFunction; ElemGraphicsPipelineStateRenderTargetSpan RenderTargets; ElemGraphicsPipelineStateDepthStencil DepthStencil; + ElemGraphicsFillMode FillMode; + ElemGraphicsCullMode CullMode; // Optional debug name for the pipeline state. const char* DebugName; } ElemGraphicsPipelineStateParameters; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index b72da555..8f23ca27 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -104,6 +104,33 @@ bool IsDirectX12BlendEnabled(ElemGraphicsPipelineStateRenderTarget renderTargetP renderTargetParameters.DestinationBlendFactorAlpha == ElemGraphicsBlendFactor_Zero); } +D3D12_FILL_MODE ConvertToDirectX12FillMode(ElemGraphicsFillMode fillMode) +{ + switch (fillMode) + { + case ElemGraphicsFillMode_Solid: + return D3D12_FILL_MODE_SOLID; + + case ElemGraphicsFillMode_Wireframe: + return D3D12_FILL_MODE_WIREFRAME; + } +} + +D3D12_CULL_MODE ConvertToDirectX12CullMode(ElemGraphicsCullMode cullMode) +{ + switch (cullMode) + { + case ElemGraphicsCullMode_BackFace: + return D3D12_CULL_MODE_BACK; + + case ElemGraphicsCullMode_FrontFace: + return D3D12_CULL_MODE_FRONT; + + case ElemGraphicsCullMode_None: + return D3D12_CULL_MODE_NONE; + } +} + D3D12_BLEND_OP ConvertToDirectX12BlendOperation(ElemGraphicsBlendOperation blendOperation) { switch (blendOperation) @@ -112,16 +139,16 @@ D3D12_BLEND_OP ConvertToDirectX12BlendOperation(ElemGraphicsBlendOperation blend return D3D12_BLEND_OP_ADD; case ElemGraphicsBlendOperation_Substract: - return D3D12_BLEND_OP_ADD; + return D3D12_BLEND_OP_SUBTRACT; case ElemGraphicsBlendOperation_ReverseSubstract: - return D3D12_BLEND_OP_ADD; + return D3D12_BLEND_OP_REV_SUBTRACT; case ElemGraphicsBlendOperation_Min: - return D3D12_BLEND_OP_ADD; + return D3D12_BLEND_OP_MIN; case ElemGraphicsBlendOperation_Max: - return D3D12_BLEND_OP_ADD; + return D3D12_BLEND_OP_MAX; } } @@ -295,9 +322,8 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev sampleDesc.Quality = 0; D3D12_RASTERIZER_DESC2 rasterizerState = {}; - //rasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME; - rasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - rasterizerState.CullMode = D3D12_CULL_MODE_NONE; // D3D12_CULL_MODE_BACK; + rasterizerState.FillMode = ConvertToDirectX12FillMode(parameters->FillMode); + rasterizerState.CullMode = ConvertToDirectX12CullMode(parameters->CullMode); rasterizerState.FrontCounterClockwise = false; rasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; rasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 5fdef375..22d5f271 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -2,12 +2,12 @@ #include "GraphicsTests.h" #include "utest.h" +// TODO: Test Amplification Shader // TODO: Test Viewports // TODO: Test Multiple render targets // TODO: Render on a render target texture // TODO: Check command list type when dispatch mesh // TODO: Multiple config for rendering -// TODO: Test Barrier log UTEST(Rendering, RenderPassClearRenderTarget) { diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index dde229dc..6e17c953 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -5,13 +5,8 @@ // TODO: Validate dispatch thread group count // TODO: Cannot push constant before binding pso // TODO: PSO Multi render targets (Test Blend States too) -// TODO: PSO Blend state -// TODO: PSO FillMode -// TODO: PSO Cull order -// TODO: PSO Depth Bias? // TODO: Check depth stencil format if comparaison function set - UTEST(Shader, CompileComputePipelineState) { // Arrange @@ -107,6 +102,164 @@ UTEST(Shader, DispatchCompute) } } +struct Shader_CompileGraphicsPipelineStateFillAndCullMode +{ + ElemGraphicsFillMode FillMode; + ElemGraphicsCullMode CullMode; + bool TwoColors; + float Color[3]; + float Color2[3]; +}; + +UTEST_F_SETUP(Shader_CompileGraphicsPipelineStateFillAndCullMode) +{ +} + +UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateFillAndCullMode) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 }, + .FillMode = utest_fixture->FillMode, + .CullMode = utest_fixture->CullMode + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "ShaderTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + + float shaderParameters[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)shaderParameters, .Length = sizeof(float) * 8 }); + ElemDispatchMesh(commandList, 1, 1, 1); + + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto floatData = (float*)bufferData.Items; + bool colorMatch[2] = {}; + + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) + { + if (!utest_fixture->TwoColors) + { + ASSERT_EQ_MSG(floatData[i], utest_fixture->Color[0], "Red channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 1], utest_fixture->Color[1], "Green channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 2], utest_fixture->Color[2], "Blue channel data is invalid."); + ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); + } + else + { + if (floatData[i] == utest_fixture->Color[0] && + floatData[i + 1] == utest_fixture->Color[1] && + floatData[i + 2] == utest_fixture->Color[2]) + { + colorMatch[0] = true; + } + else if(floatData[i] == utest_fixture->Color2[0] && + floatData[i + 1] == utest_fixture->Color2[1] && + floatData[i + 2] == utest_fixture->Color2[2]) + { + colorMatch[1] = true; + } + } + } + + if (utest_fixture->TwoColors) + { + ASSERT_TRUE_MSG(colorMatch[0], "First color must be present."); + ASSERT_TRUE_MSG(colorMatch[1], "Second color must be present."); + } +} + +UTEST_F(Shader_CompileGraphicsPipelineStateFillAndCullMode, FillModeSolid) +{ + utest_fixture->FillMode = ElemGraphicsFillMode_Solid; + utest_fixture->Color[0] = 1.0f; + utest_fixture->Color[1] = 1.0f; + utest_fixture->Color[2] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateFillAndCullMode, FillModeWireframe) +{ + utest_fixture->FillMode = ElemGraphicsFillMode_Wireframe; + + utest_fixture->TwoColors = true; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 1.0f; + + utest_fixture->Color2[0] = 1.0f; + utest_fixture->Color2[1] = 1.0f; + utest_fixture->Color2[2] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateFillAndCullMode, CullModeBackface) +{ + utest_fixture->CullMode = ElemGraphicsCullMode_BackFace; + utest_fixture->Color[0] = 1.0f; + utest_fixture->Color[1] = 1.0f; + utest_fixture->Color[2] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateFillAndCullMode, CullModeFrontFace) +{ + utest_fixture->CullMode = ElemGraphicsCullMode_FrontFace; + utest_fixture->Color[0] = 0.0f; + utest_fixture->Color[1] = 0.0f; + utest_fixture->Color[2] = 1.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateFillAndCullMode, CullModeNone) +{ + utest_fixture->CullMode = ElemGraphicsCullMode_None; + utest_fixture->Color[0] = 1.0f; + utest_fixture->Color[1] = 1.0f; + utest_fixture->Color[2] = 0.0f; +} + struct Shader_CompileGraphicsPipelineStateDepthCompare { float ClearDepthValue; @@ -794,4 +947,210 @@ UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_SourceAlphaSaturated_ utest_fixture->ResultColor[3] = 0.0f; } -// TODO: Other blend operators +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Substract_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Substract; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 0.25f; + utest_fixture->DestinationColor[1] = 0.5f; + utest_fixture->DestinationColor[2] = 0.75f; + utest_fixture->DestinationColor[3] = 0.5f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 1.0f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.75f; + utest_fixture->ResultColor[1] = 0.5f; + utest_fixture->ResultColor[2] = 0.25f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, ReverseSubstract_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_ReverseSubstract; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.75f; + utest_fixture->ResultColor[1] = 0.5f; + utest_fixture->ResultColor[2] = 0.25f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Min_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Min; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.25f; + utest_fixture->ResultColor[1] = 0.5f; + utest_fixture->ResultColor[2] = 0.75f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Max_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Max; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_Zero; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 1.0f; + utest_fixture->ResultColor[1] = 1.0f; + utest_fixture->ResultColor[2] = 1.0f; + utest_fixture->ResultColor[3] = 0.0f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaSubstract_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Substract; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 0.25f; + utest_fixture->DestinationColor[1] = 0.5f; + utest_fixture->DestinationColor[2] = 0.75f; + utest_fixture->DestinationColor[3] = 0.5f; + + utest_fixture->SourceColor[0] = 1.0f; + utest_fixture->SourceColor[1] = 1.0f; + utest_fixture->SourceColor[2] = 1.0f; + utest_fixture->SourceColor[3] = 1.0f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.5f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaReverseSubstract_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_ReverseSubstract; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.5f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaMin_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Min; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 0.5f; +} + +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaMax_One_One) +{ + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; + utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; + utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; + + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Max; + utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; + utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; + + utest_fixture->DestinationColor[0] = 1.0f; + utest_fixture->DestinationColor[1] = 1.0f; + utest_fixture->DestinationColor[2] = 1.0f; + utest_fixture->DestinationColor[3] = 1.0f; + + utest_fixture->SourceColor[0] = 0.25f; + utest_fixture->SourceColor[1] = 0.5f; + utest_fixture->SourceColor[2] = 0.75f; + utest_fixture->SourceColor[3] = 0.5f; + + utest_fixture->ResultColor[0] = 0.0f; + utest_fixture->ResultColor[1] = 0.0f; + utest_fixture->ResultColor[2] = 0.0f; + utest_fixture->ResultColor[3] = 1.0f; +} From 2515eb5ee690a4f8b3fa5643cc93a365199194d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 22 Aug 2024 17:14:46 +0200 Subject: [PATCH 12/93] Add tests for Amplification shaders --- src/Elemental/Elemental.h | 1 + .../Microsoft/Graphics/DirectX12Shader.cpp | 12 ++ tests/GraphicsTests/GraphicsTests.cpp | 8 +- tests/GraphicsTests/GraphicsTests.h | 14 ++ tests/GraphicsTests/RenderingTests.cpp | 144 +++++++++++++++--- tests/GraphicsTests/ShaderTests.cpp | 11 +- .../GraphicsTests/Shaders/RenderingTests.hlsl | 24 ++- 7 files changed, 182 insertions(+), 32 deletions(-) diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 757a4ffe..64b97a90 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -752,6 +752,7 @@ typedef struct { // Shader library containing the shaders. ElemShaderLibrary ShaderLibrary; + const char* AmplificationShaderFunction; // Function name of the mesh shader in the shader library. const char* MeshShaderFunction; // Function name of the pixel shader in the shader library. diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 8f23ca27..7a69df6c 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -340,6 +340,18 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev psoDesc.RootSignature = graphicsDeviceData->RootSignature.Get(); + if (parameters->AmplificationShaderFunction) + { + auto shaderByteCode = GetDirectX12ShaderFunctionByteCode(shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); + + if (!shaderByteCode.pShaderBytecode) + { + return ELEM_HANDLE_NULL; + } + + psoDesc.AS = shaderByteCode; + } + if (parameters->MeshShaderFunction) { auto shaderByteCode = GetDirectX12ShaderFunctionByteCode(shaderLibraryData, ShaderType_Mesh, parameters->MeshShaderFunction); diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index 78525987..a909e812 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -298,13 +298,14 @@ ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const return pipelineState; } -ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) +ElemPipelineState TestOpenMeshShaderAmplification(ElemGraphicsDevice graphicsDevice, const char* shader, const char* amplificationShaderFunction, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) { auto shaderLibrary = TestOpenShader(graphicsDevice, shader); ElemGraphicsPipelineStateParameters pipelineStateParameters = *baseParameters; pipelineStateParameters.ShaderLibrary = shaderLibrary; + pipelineStateParameters.AmplificationShaderFunction = amplificationShaderFunction; pipelineStateParameters.MeshShaderFunction = meshShaderFunction; pipelineStateParameters.PixelShaderFunction = pixelShaderFunction; @@ -319,6 +320,11 @@ ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const ch return pipelineState; } +ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) +{ + return TestOpenMeshShaderAmplification(graphicsDevice, shader, nullptr, meshShaderFunction, pixelShaderFunction, baseParameters); +} + TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType) { ElemGraphicsHeapOptions heapOptions = diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index f01db9a4..efa47f3f 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -14,6 +14,19 @@ #define ASSERT_LOG_MESSAGE(message) { TestInitLog(); ASSERT_TRUE_MSG(strstr(testErrorLogs, message) != NULL, message); } #define ASSERT_LOG_MESSAGE_DEBUG(message) { TestInitLog(); ASSERT_TRUE_MSG(strstr(testDebugLogs, message) != NULL, message); } +#define ASSERT_COLOR_BUFFER(buffer, red, green, blue, alpha) \ + { \ + auto floatData = (float*)buffer.Items; \ + \ + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) \ + { \ + ASSERT_EQ_MSG(floatData[i], red, "Red channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 1], green, "Green channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 2], blue, "Blue channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 3], alpha, "Alpha channel data is invalid."); \ + } \ + } + #define BUFFER_BARRIER(resource, syncBefore, syncAfter, accessBefore, accessAfter) \ { \ .Resource = resource, \ @@ -128,6 +141,7 @@ void TestInitLog(); ElemShaderLibrary TestOpenShader(ElemGraphicsDevice graphicsDevice, const char* shader); ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* function); +ElemPipelineState TestOpenMeshShaderAmplification(ElemGraphicsDevice graphicsDevice, const char* shader, const char* amplificationShaderFunction, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_Gpu); diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 22d5f271..9d1ddbab 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -2,10 +2,8 @@ #include "GraphicsTests.h" #include "utest.h" -// TODO: Test Amplification Shader // TODO: Test Viewports // TODO: Test Multiple render targets -// TODO: Render on a render target texture // TODO: Check command list type when dispatch mesh // TODO: Multiple config for rendering @@ -54,16 +52,7 @@ UTEST(Rendering, RenderPassClearRenderTarget) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - - auto floatData = (float*)bufferData.Items; - - for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) - { - ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 1], 0.5f, "Green channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 2], 0.25f, "Blue channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 3], 0.95f, "Alpha channel data is invalid."); - } + ASSERT_COLOR_BUFFER(bufferData, 1.0f, 0.5f, 0.25f, 0.95f); } UTEST(Rendering, RenderPassClearDepthBuffer) @@ -170,16 +159,131 @@ UTEST(Rendering, DispatchMesh) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER(bufferData, 1.0f, 1.0f, 0.0f, 1.0f); +} - auto floatData = (float*)bufferData.Items; +UTEST(Rendering, DispatchMeshAmplificationShouldDraw) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShaderAmplification(graphicsDevice, "RenderingTests.shader", "AmplificationShader", "MeshShader", "PixelShader", &psoParameters); - for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = { - ASSERT_EQ_MSG(floatData[i], 1.0f, "Red channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 1], 1.0f, "Green channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 2], 0.0f, "Blue channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); - } + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + + uint32_t shouldDraw = 1u; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)&shouldDraw, .Length = sizeof(uint32_t) }); + + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER(bufferData, 1.0f, 1.0f, 0.0f, 1.0f); } -// TODO: Test Depth buffer (Clear with a specific value and rendering) +UTEST(Rendering, DispatchMeshAmplificationShouldNotDraw) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShaderAmplification(graphicsDevice, "RenderingTests.shader", "AmplificationShader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + + uint32_t shouldDraw = 0u; + ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)&shouldDraw, .Length = sizeof(uint32_t) }); + + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER(bufferData, 0.0f, 0.0f, 1.0f, 1.0f); +} diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index 6e17c953..76c81941 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -351,16 +351,7 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - - auto floatData = (float*)bufferData.Items; - - for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) - { - ASSERT_EQ_MSG(floatData[i], utest_fixture->Color[0], "Red channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 1], utest_fixture->Color[1], "Green channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 2], utest_fixture->Color[2], "Blue channel data is invalid."); - ASSERT_EQ_MSG(floatData[i + 3], 1.0f, "Alpha channel data is invalid."); - } + ASSERT_COLOR_BUFFER(bufferData, utest_fixture->Color[0], utest_fixture->Color[1], utest_fixture->Color[2], 1.0f); } UTEST_F(Shader_CompileGraphicsPipelineStateDepthCompare, Never) diff --git a/tests/GraphicsTests/Shaders/RenderingTests.hlsl b/tests/GraphicsTests/Shaders/RenderingTests.hlsl index 6080ffe9..558827ce 100644 --- a/tests/GraphicsTests/Shaders/RenderingTests.hlsl +++ b/tests/GraphicsTests/Shaders/RenderingTests.hlsl @@ -1,3 +1,11 @@ +struct TestParameters +{ + uint ShouldDraw; +}; + +[[vk::push_constant]] +TestParameters parameters : register(b0); + struct VertexOutput { float4 Position: SV_Position; @@ -17,10 +25,24 @@ static uint3 rectanglePrimitives[] = uint3(2, 1, 3) }; +struct AmplificationPayload +{ + float Test; +}; + +groupshared AmplificationPayload sharedPayload; + +[shader("amplification")] +[numthreads(1, 1, 1)] +void AmplificationShader(in uint3 groupID : SV_GroupID) +{ + DispatchMesh(parameters.ShouldDraw, 1, 1, sharedPayload); +} + [shader("mesh")] [OutputTopology("triangle")] [NumThreads(4, 1, 1)] -void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) +void MeshShader(in uint groupThreadId : SV_GroupThreadID, in payload AmplificationPayload payload, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) { const uint meshVertexCount = 4; const uint meshPrimitiveCount = 2; From cd15fdd8a6e8fb251d63ff6c0ab4ad6e67ce22a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 23 Aug 2024 12:12:25 +0200 Subject: [PATCH 13/93] Implement Vulkan PSO and Depth buffer changes --- src/Elemental/Common/Graphics/Resource.cpp | 12 +- src/Elemental/Common/Graphics/Resource.h | 5 + src/Elemental/Common/Graphics/Shader.cpp | 12 +- src/Elemental/Common/Graphics/Shader.h | 5 + .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 1 + .../Graphics/Vulkan/VulkanRendering.cpp | 148 ++++++++--- .../Common/Graphics/Vulkan/VulkanResource.cpp | 51 +++- .../Common/Graphics/Vulkan/VulkanResource.h | 1 + .../Graphics/Vulkan/VulkanResourceBarrier.cpp | 33 ++- .../Common/Graphics/Vulkan/VulkanShader.cpp | 245 ++++++++++++++---- .../Graphics/Vulkan/VulkanSwapChain.cpp | 2 +- .../Graphics/DirectX12GraphicsDevice.cpp | 2 + .../Microsoft/Graphics/DirectX12Rendering.cpp | 1 + .../Microsoft/Graphics/DirectX12Resource.cpp | 13 +- .../Microsoft/Graphics/DirectX12Shader.cpp | 19 +- 15 files changed, 436 insertions(+), 114 deletions(-) create mode 100644 src/Elemental/Common/Graphics/Resource.h create mode 100644 src/Elemental/Common/Graphics/Shader.h diff --git a/src/Elemental/Common/Graphics/Resource.cpp b/src/Elemental/Common/Graphics/Resource.cpp index 8954cb07..3241f5ad 100644 --- a/src/Elemental/Common/Graphics/Resource.cpp +++ b/src/Elemental/Common/Graphics/Resource.cpp @@ -1,6 +1,16 @@ -#include "Elemental.h" +#include "Resource.h" #include "GraphicsCommon.h" +bool CheckDepthStencilFormat(ElemGraphicsFormat format) +{ + if (format == ElemGraphicsFormat_D32_FLOAT) + { + return true; + } + + return false; +} + ElemAPI ElemGraphicsHeap ElemCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes, const ElemGraphicsHeapOptions* options) { DispatchReturnGraphicsFunction(CreateGraphicsHeap, graphicsDevice, sizeInBytes, options); diff --git a/src/Elemental/Common/Graphics/Resource.h b/src/Elemental/Common/Graphics/Resource.h new file mode 100644 index 00000000..beb78e11 --- /dev/null +++ b/src/Elemental/Common/Graphics/Resource.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../Elemental.h" + +bool CheckDepthStencilFormat(ElemGraphicsFormat format); diff --git a/src/Elemental/Common/Graphics/Shader.cpp b/src/Elemental/Common/Graphics/Shader.cpp index bc37e772..7f109607 100644 --- a/src/Elemental/Common/Graphics/Shader.cpp +++ b/src/Elemental/Common/Graphics/Shader.cpp @@ -1,6 +1,16 @@ -#include "Elemental.h" +#include "Shader.h" #include "GraphicsCommon.h" +bool IsBlendEnabled(ElemGraphicsPipelineStateRenderTarget renderTargetParameters) +{ + return !(renderTargetParameters.BlendOperation == ElemGraphicsBlendOperation_Add && + renderTargetParameters.SourceBlendFactor == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.DestinationBlendFactor == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.BlendOperationAlpha == ElemGraphicsBlendOperation_Add && + renderTargetParameters.SourceBlendFactorAlpha == ElemGraphicsBlendFactor_Zero && + renderTargetParameters.DestinationBlendFactorAlpha == ElemGraphicsBlendFactor_Zero); +} + ElemAPI ElemShaderLibrary ElemCreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) { DispatchReturnGraphicsFunction(CreateShaderLibrary, graphicsDevice, shaderLibraryData); diff --git a/src/Elemental/Common/Graphics/Shader.h b/src/Elemental/Common/Graphics/Shader.h new file mode 100644 index 00000000..eff51299 --- /dev/null +++ b/src/Elemental/Common/Graphics/Shader.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../Elemental.h" + +bool IsBlendEnabled(ElemGraphicsPipelineStateRenderTarget renderTargetParameters); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index f9374d4e..4c1af54a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -568,6 +568,7 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o features.features.shaderInt16 = true; features.features.shaderInt64 = true; features.features.pipelineStatisticsQuery = true; + features.features.fillModeNonSolid = true; VkPhysicalDeviceVulkan12Features features12 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; features12.timelineSemaphore = true; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp index 6882c41a..3b690c19 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp @@ -3,9 +3,48 @@ #include "VulkanCommandList.h" #include "VulkanResource.h" #include "VulkanResourceBarrier.h" -#include "SystemLogging.h" #include "SystemFunctions.h" +VkAttachmentLoadOp ConvertToVulkanRenderPassAttachmentLoadOp(ElemRenderPassLoadAction loadAction) +{ + VkAttachmentLoadOp loadOperation; + + switch (loadAction) + { + case ElemRenderPassLoadAction_Load: + loadOperation = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + + case ElemRenderPassLoadAction_Clear: + loadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + + default: + loadOperation = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + } + + return loadOperation; +} + +VkAttachmentStoreOp ConvertToVulkanRenderPassAttachmentStoreOp(ElemRenderPassStoreAction storeAction) +{ + VkAttachmentStoreOp storeOperation; + + switch (storeAction) + { + case ElemRenderPassStoreAction_Store: + storeOperation = VK_ATTACHMENT_STORE_OP_STORE; + break; + + default: + storeOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; + break; + } + + return storeOperation; +} + void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPassParameters* parameters) { // TODO: Check command list type != COMPUTE @@ -21,8 +60,10 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas auto commandListDataFull = GetVulkanCommandListDataFull(commandList); SystemAssert(commandListDataFull); commandListDataFull->CurrentRenderPassParameters = *parameters; + + auto depthStencilCount = (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) ? 1 : 0; - auto renderingAttachments = SystemPushArray(stackMemoryArena, parameters->RenderTargets.Length); + auto renderingAttachments = SystemPushArray(stackMemoryArena, parameters->RenderTargets.Length + depthStencilCount); auto renderingWidth = 0u; auto renderingHeight = 0u; @@ -34,23 +75,8 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas auto textureData = GetVulkanGraphicsResourceData(renderTargetParameters.RenderTarget); SystemAssert(textureData); - VkAttachmentLoadOp loadOperation; + auto loadOperation = ConvertToVulkanRenderPassAttachmentLoadOp(renderTargetParameters.LoadAction); - switch (renderTargetParameters.LoadAction) - { - case ElemRenderPassLoadAction_Load: - loadOperation = VK_ATTACHMENT_LOAD_OP_LOAD; - break; - - case ElemRenderPassLoadAction_Clear: - loadOperation = VK_ATTACHMENT_LOAD_OP_CLEAR; - break; - - default: - loadOperation = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - break; - } - VkClearValue clearValue { .color = @@ -62,18 +88,7 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas }} }; - VkAttachmentStoreOp storeOperation; - - switch (renderTargetParameters.StoreAction) - { - case ElemRenderPassStoreAction_Store: - storeOperation = VK_ATTACHMENT_STORE_OP_STORE; - break; - - default: - storeOperation = VK_ATTACHMENT_STORE_OP_DONT_CARE; - break; - } + auto storeOperation = ConvertToVulkanRenderPassAttachmentStoreOp(renderTargetParameters.StoreAction); renderingAttachments[i] = { @@ -122,16 +137,66 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas ElemSetViewports(commandList, parameters->Viewports); } - // TODO: Take into account the depth buffer because it is possible to render only - // to the depth buffer without any color render targets + if (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) + { + auto depthStencilParameters = parameters->DepthStencil; + + auto textureData = GetVulkanGraphicsResourceData(depthStencilParameters.DepthStencil); + SystemAssert(textureData); + + if (renderingWidth == 0 || renderingHeight == 0) + { + renderingWidth = textureData->Width; + renderingHeight = textureData->Height; + } + + // TODO: Validate usage + auto loadOperation = ConvertToVulkanRenderPassAttachmentLoadOp(depthStencilParameters.DepthLoadAction); + auto storeOperation = ConvertToVulkanRenderPassAttachmentStoreOp(depthStencilParameters.DepthStoreAction); + + VkClearValue clearValue + { + .depthStencil = + { + .depth = depthStencilParameters.DepthClearValue + } + }; + + renderingAttachments[parameters->RenderTargets.Length] = + { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = textureData->DepthStencilImageView, + .imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + .loadOp = loadOperation, + .storeOp = storeOperation, + .clearValue = clearValue + }; + + ResourceBarrierItem resourceBarrier = + { + .Type = ElemGraphicsResourceType_Texture2D, + .IsDepthStencil = true, + .Resource = depthStencilParameters.DepthStencil, + .AfterAccess = ElemGraphicsResourceBarrierAccessType_DepthStencilWrite, + .AfterLayout = ElemGraphicsResourceBarrierLayoutType_DepthStencilWrite + }; + + EnqueueBarrier(commandListData->ResourceBarrierPool, &resourceBarrier); + } + SystemAssert(renderingWidth != 0 && renderingHeight != 0); VkRenderingInfo renderingInfo = { VK_STRUCTURE_TYPE_RENDERING_INFO }; renderingInfo.renderArea.extent.width = renderingWidth; renderingInfo.renderArea.extent.height = renderingHeight; renderingInfo.layerCount = 1; - renderingInfo.colorAttachmentCount = renderingAttachments.Length; + renderingInfo.colorAttachmentCount = parameters->RenderTargets.Length; renderingInfo.pColorAttachments = renderingAttachments.Pointer; + + if (depthStencilCount > 0) + { + renderingInfo.pDepthAttachment = &renderingAttachments.Pointer[parameters->RenderTargets.Length]; + } InsertVulkanResourceBarriersIfNeeded(commandList, ElemGraphicsResourceBarrierSyncType_RenderTarget); vkCmdBeginRendering(commandListData->DeviceObject, &renderingInfo); @@ -180,6 +245,23 @@ void VulkanEndRenderPass(ElemCommandList commandList) commandQueueData->SignalPresentSemaphore = true; } } + + // HACK: Review this, it is needed to avoid a validation error but I don't see why + // we need to transition the depth buffer to read here. + if (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) + { + ResourceBarrierItem resourceBarrier = + { + .Type = ElemGraphicsResourceType_Texture2D, + .IsDepthStencil = true, + .Resource = parameters->DepthStencil.DepthStencil, + .AfterSync = ElemGraphicsResourceBarrierSyncType_RenderTarget, + .AfterAccess = ElemGraphicsResourceBarrierAccessType_NoAccess, + .AfterLayout = ElemGraphicsResourceBarrierLayoutType_Read + }; + + EnqueueBarrier(commandListData->ResourceBarrierPool, &resourceBarrier); + } InsertVulkanResourceBarriersIfNeeded(commandList, ElemGraphicsResourceBarrierSyncType_None); } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 77f6f6d8..9ffb843a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -1,5 +1,6 @@ #include "VulkanResource.h" #include "VulkanGraphicsDevice.h" +#include "Graphics/Resource.h" #include "Graphics/ResourceDeleteQueue.h" #include "SystemDataPool.h" #include "SystemFunctions.h" @@ -56,6 +57,9 @@ VkFormat ConvertToVulkanTextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_R32G32B32A32_FLOAT: return VK_FORMAT_R32G32B32A32_SFLOAT; + + case ElemGraphicsFormat_D32_FLOAT: + return VK_FORMAT_D32_SFLOAT; case ElemGraphicsFormat_Raw: return VK_FORMAT_UNDEFINED; @@ -75,6 +79,11 @@ VkImageUsageFlags ConvertToVulkanImageUsageFlags(ElemGraphicsResourceUsage usage { result |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } + + if (usage & ElemGraphicsResourceUsage_DepthStencil) + { + result |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + } return result; } @@ -92,6 +101,7 @@ ElemGraphicsResource CreateVulkanTextureFromResource(ElemGraphicsDevice graphics auto vulkanTextureFormat = ConvertToVulkanTextureFormat(resourceInfo->Format); VkImageView renderTargetImageView = {}; + VkImageView depthStencilImageView = {}; if (resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget) { @@ -106,11 +116,25 @@ ElemGraphicsResource CreateVulkanTextureFromResource(ElemGraphicsDevice graphics AssertIfFailed(vkCreateImageView(graphicsDeviceData->Device, &createInfo, 0, &renderTargetImageView)); } + else if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil) + { + VkImageViewCreateInfo createInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + createInfo.image = resource; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = vulkanTextureFormat; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; // TODO: Maybe we need to combine that with flag Stencil depending on the format + createInfo.subresourceRange.baseMipLevel = 0; + createInfo.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + createInfo.subresourceRange.layerCount = 1; + + AssertIfFailed(vkCreateImageView(graphicsDeviceData->Device, &createInfo, 0, &depthStencilImageView)); + } auto handle = SystemAddDataPoolItem(vulkanGraphicsResourcePool, { .TextureDeviceObject = resource, .Type = ElemGraphicsResourceType_Texture2D, .RenderTargetImageView = renderTargetImageView, + .DepthStencilImageView = depthStencilImageView, .Format = vulkanTextureFormat, .InternalFormat = resourceInfo->Format, .IsPresentTexture = isPresentTexture, @@ -355,6 +379,18 @@ ElemGraphicsResource VulkanCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, return ELEM_HANDLE_NULL; } + if ((resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget) && (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage RenderTarget and DepthStencil should not be used together."); + return ELEM_HANDLE_NULL; + } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil && !CheckDepthStencilFormat(resourceInfo->Format)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage DepthStencil should use a compatible format."); + return ELEM_HANDLE_NULL; + } + auto texture = CreateVulkanTexture(graphicsHeapData->GraphicsDevice, resourceInfo); AssertIfFailed(vkBindImageMemory(graphicsDeviceData->Device, texture, graphicsHeapData->DeviceObject, graphicsHeapOffset)); @@ -373,6 +409,12 @@ ElemGraphicsResource VulkanCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to RenderTarget."); return ELEM_HANDLE_NULL; } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to DepthStencil."); + return ELEM_HANDLE_NULL; + } auto buffer = CreateVulkanBuffer(graphicsHeapData->GraphicsDevice, resourceInfo); @@ -425,6 +467,11 @@ void VulkanFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGra vkDestroyImageView(graphicsDeviceData->Device, resourceData->RenderTargetImageView, nullptr); } + if (resourceData->Usage & ElemGraphicsResourceUsage_DepthStencil) + { + vkDestroyImageView(graphicsDeviceData->Device, resourceData->DepthStencilImageView, nullptr); + } + if (resourceData->TextureDeviceObject && !resourceData->IsPresentTexture) { vkDestroyImage(graphicsDeviceData->Device, resourceData->TextureDeviceObject, nullptr); @@ -551,11 +598,11 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi createInfo.image = resourceData->TextureDeviceObject; createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; createInfo.format = resourceData->Format; - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.aspectMask = (resourceData->Usage & ElemGraphicsResourceUsage_DepthStencil) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; // TODO: Stencil? createInfo.subresourceRange.baseMipLevel = textureMipIndex; createInfo.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; createInfo.subresourceRange.layerCount = 1; - + VkImageView imageView; AssertIfFailed(vkCreateImageView(graphicsDeviceData->Device, &createInfo, 0, &imageView)); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index a2ae99f3..2770199a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -17,6 +17,7 @@ struct VulkanGraphicsResourceData VkImage TextureDeviceObject; ElemGraphicsResourceType Type; VkImageView RenderTargetImageView; + VkImageView DepthStencilImageView; VkFormat Format; ElemGraphicsFormat InternalFormat; bool IsPresentTexture; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp index 02626771..ac27a957 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp @@ -4,7 +4,7 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -VkPipelineStageFlags2 ConvertToVulkanBarrierSync(ElemGraphicsResourceBarrierSyncType syncType) +VkPipelineStageFlags2 ConvertToVulkanBarrierSync(ElemGraphicsResourceBarrierSyncType syncType, bool isDepthStencil) { switch (syncType) { @@ -15,7 +15,20 @@ VkPipelineStageFlags2 ConvertToVulkanBarrierSync(ElemGraphicsResourceBarrierSync return VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; case ElemGraphicsResourceBarrierSyncType_RenderTarget: - return VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_EXT | VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + { + if (!isDepthStencil) + { + return VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_EXT | + VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT | + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; + } + else + { + return VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT; + } + } } } @@ -35,6 +48,9 @@ VkAccessFlags2 ConvertToVulkanBarrierAccess(ElemGraphicsResourceBarrierAccessTyp case ElemGraphicsResourceBarrierAccessType_RenderTarget: return VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT; + case ElemGraphicsResourceBarrierAccessType_DepthStencilWrite: + return VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + case ElemGraphicsResourceBarrierAccessType_Write: return VK_ACCESS_2_SHADER_WRITE_BIT; } @@ -60,6 +76,9 @@ VkImageLayout ConvertToVulkanBarrierLayout(ElemGraphicsResourceBarrierLayoutType case ElemGraphicsResourceBarrierLayoutType_RenderTarget: return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + case ElemGraphicsResourceBarrierLayoutType_DepthStencilWrite: + return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + case ElemGraphicsResourceBarrierLayoutType_Present: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; } @@ -101,8 +120,8 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph vulkanBufferBarrier->sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; vulkanBufferBarrier->buffer = graphicsResourceData->BufferDeviceObject; vulkanBufferBarrier->size = graphicsResourceData->Width; - vulkanBufferBarrier->srcStageMask = ConvertToVulkanBarrierSync(barrier.BeforeSync); - vulkanBufferBarrier->dstStageMask = ConvertToVulkanBarrierSync(barrier.AfterSync); + vulkanBufferBarrier->srcStageMask = ConvertToVulkanBarrierSync(barrier.BeforeSync, false); + vulkanBufferBarrier->dstStageMask = ConvertToVulkanBarrierSync(barrier.AfterSync, false); vulkanBufferBarrier->srcAccessMask = ConvertToVulkanBarrierAccess(barrier.BeforeAccess); vulkanBufferBarrier->dstAccessMask = ConvertToVulkanBarrierAccess(barrier.AfterAccess); } @@ -124,13 +143,13 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph vulkanTextureBarrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; vulkanTextureBarrier->image = graphicsResourceData->TextureDeviceObject; - vulkanTextureBarrier->srcStageMask = ConvertToVulkanBarrierSync(barrier.BeforeSync); - vulkanTextureBarrier->dstStageMask = ConvertToVulkanBarrierSync(barrier.AfterSync); + vulkanTextureBarrier->srcStageMask = ConvertToVulkanBarrierSync(barrier.BeforeSync, barrier.IsDepthStencil); + vulkanTextureBarrier->dstStageMask = ConvertToVulkanBarrierSync(barrier.AfterSync, barrier.IsDepthStencil); vulkanTextureBarrier->srcAccessMask = ConvertToVulkanBarrierAccess(barrier.BeforeAccess); vulkanTextureBarrier->dstAccessMask = ConvertToVulkanBarrierAccess(barrier.AfterAccess); vulkanTextureBarrier->oldLayout = ConvertToVulkanBarrierLayout(barrier.BeforeLayout); vulkanTextureBarrier->newLayout = ConvertToVulkanBarrierLayout(barrier.AfterLayout); - vulkanTextureBarrier->subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + vulkanTextureBarrier->subresourceRange.aspectMask = graphicsResourceData->Usage & ElemGraphicsResourceUsage_DepthStencil ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; vulkanTextureBarrier->subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; vulkanTextureBarrier->subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 35f43d28..8cb36e75 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -3,6 +3,8 @@ #include "VulkanCommandList.h" #include "VulkanResource.h" #include "VulkanResourceBarrier.h" +#include "Graphics/Resource.h" +#include "Graphics/Shader.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" @@ -83,6 +85,123 @@ VkShaderStageFlagBits ConvertShaderTypeToVulkan(ShaderType shaderType) } } +VkPolygonMode ConvertToVulkanPolygonMode(ElemGraphicsFillMode fillMode) +{ + switch (fillMode) + { + case ElemGraphicsFillMode_Solid: + return VK_POLYGON_MODE_FILL; + + case ElemGraphicsFillMode_Wireframe: + return VK_POLYGON_MODE_LINE; + } +} + +VkCullModeFlags ConvertToVulkanCullMode(ElemGraphicsCullMode cullMode) +{ + switch (cullMode) + { + case ElemGraphicsCullMode_BackFace: + return VK_CULL_MODE_BACK_BIT; + + case ElemGraphicsCullMode_FrontFace: + return VK_CULL_MODE_FRONT_BIT; + + case ElemGraphicsCullMode_None: + return VK_CULL_MODE_NONE; + } +} + +VkBlendOp ConvertToVulkanBlendOperation(ElemGraphicsBlendOperation blendOperation) +{ + switch (blendOperation) + { + case ElemGraphicsBlendOperation_Add: + return VK_BLEND_OP_ADD; + + case ElemGraphicsBlendOperation_Substract: + return VK_BLEND_OP_SUBTRACT; + + case ElemGraphicsBlendOperation_ReverseSubstract: + return VK_BLEND_OP_REVERSE_SUBTRACT; + + case ElemGraphicsBlendOperation_Min: + return VK_BLEND_OP_MIN; + + case ElemGraphicsBlendOperation_Max: + return VK_BLEND_OP_MAX; + } +} + +VkBlendFactor ConvertToVulkanBlendFactor(ElemGraphicsBlendFactor blendFactor) +{ + switch (blendFactor) + { + case ElemGraphicsBlendFactor_Zero: + return VK_BLEND_FACTOR_ZERO; + + case ElemGraphicsBlendFactor_One: + return VK_BLEND_FACTOR_ONE; + + case ElemGraphicsBlendFactor_SourceColor: + return VK_BLEND_FACTOR_SRC_COLOR; + + case ElemGraphicsBlendFactor_InverseSourceColor: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + + case ElemGraphicsBlendFactor_SourceAlpha: + return VK_BLEND_FACTOR_SRC_ALPHA; + + case ElemGraphicsBlendFactor_InverseSourceAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + + case ElemGraphicsBlendFactor_DestinationColor: + return VK_BLEND_FACTOR_DST_COLOR; + + case ElemGraphicsBlendFactor_InverseDestinationColor: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + + case ElemGraphicsBlendFactor_DestinationAlpha: + return VK_BLEND_FACTOR_DST_ALPHA; + + case ElemGraphicsBlendFactor_InverseDestinationAlpha: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + + case ElemGraphicsBlendFactor_SourceAlphaSaturated: + return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; + } +} + +VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return VK_COMPARE_OP_NEVER; + + case ElemGraphicsCompareFunction_Less: + return VK_COMPARE_OP_LESS; + + case ElemGraphicsCompareFunction_Equal: + return VK_COMPARE_OP_EQUAL; + + case ElemGraphicsCompareFunction_LessEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + + case ElemGraphicsCompareFunction_Greater: + return VK_COMPARE_OP_GREATER; + + case ElemGraphicsCompareFunction_NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + + case ElemGraphicsCompareFunction_GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + + case ElemGraphicsCompareFunction_Always: + return VK_COMPARE_OP_ALWAYS; + } +} + VkPipelineShaderStageCreateInfo GetVulkanShaderFunctionStageCreateInfo(MemoryArena memoryArena, VulkanShaderLibraryData* shaderLibraryData, ShaderType shaderType, const char* function) { SystemAssert(function); @@ -165,13 +284,88 @@ ElemPipelineState VulkanCompileGraphicsPipelineState(ElemGraphicsDevice graphics auto shaderLibraryData= GetVulkanShaderLibraryData(parameters->ShaderLibrary); SystemAssert(shaderLibraryData); + + // TODO: Extract functions + auto renderTargetFormats = SystemPushArray(stackMemoryArena, parameters->RenderTargets.Length); + auto renderTargetBlendStates = SystemPushArray(stackMemoryArena, parameters->RenderTargets.Length); - VkGraphicsPipelineCreateInfo createInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + for (uint32_t i = 0; i < parameters->RenderTargets.Length; i++) + { + auto renderTargetParameters = parameters->RenderTargets.Items[i]; + + renderTargetFormats[i] = ConvertToVulkanTextureFormat(renderTargetParameters.Format); + + renderTargetBlendStates[i] = + { + .blendEnable = IsBlendEnabled(renderTargetParameters), + .srcColorBlendFactor = ConvertToVulkanBlendFactor(renderTargetParameters.SourceBlendFactor), + .dstColorBlendFactor = ConvertToVulkanBlendFactor(renderTargetParameters.DestinationBlendFactor), + .colorBlendOp = ConvertToVulkanBlendOperation(renderTargetParameters.BlendOperation), + .srcAlphaBlendFactor = ConvertToVulkanBlendFactor(renderTargetParameters.SourceBlendFactorAlpha), + .dstAlphaBlendFactor = ConvertToVulkanBlendFactor(renderTargetParameters.DestinationBlendFactorAlpha), + .alphaBlendOp = ConvertToVulkanBlendOperation(renderTargetParameters.BlendOperationAlpha), + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT + }; + } + + VkFormat depthFormat = VK_FORMAT_UNDEFINED; + VkPipelineDepthStencilStateCreateInfo depthStencilState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + + if (CheckDepthStencilFormat(parameters->DepthStencil.Format)) + { + depthFormat = ConvertToVulkanTextureFormat(parameters->DepthStencil.Format); + depthStencilState.depthTestEnable = true; + + if (!parameters->DepthStencil.DepthDisableWrite) + { + depthStencilState.depthWriteEnable = true; + } + + depthStencilState.depthCompareOp = ConvertToVulkanCompareFunction(parameters->DepthStencil.DepthCompareFunction); + } + + VkPipelineRenderingCreateInfo renderingCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + renderingCreateInfo.colorAttachmentCount = renderTargetFormats.Length; + renderingCreateInfo.pColorAttachmentFormats = renderTargetFormats.Pointer; + renderingCreateInfo.depthAttachmentFormat = depthFormat; + + VkPipelineColorBlendStateCreateInfo colorBlendState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + colorBlendState.attachmentCount = renderTargetBlendStates.Length; + colorBlendState.pAttachments = renderTargetBlendStates.Pointer; + + VkPipelineMultisampleStateCreateInfo multisampleState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineRasterizationStateCreateInfo rasterizationState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + rasterizationState.polygonMode = ConvertToVulkanPolygonMode(parameters->FillMode); + rasterizationState.cullMode = ConvertToVulkanCullMode(parameters->CullMode); + rasterizationState.frontFace = VK_FRONT_FACE_CLOCKWISE; + rasterizationState.lineWidth = 1; + + VkPipelineViewportStateCreateInfo viewportState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + + VkPipelineDynamicStateCreateInfo dynamicState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; + dynamicState.dynamicStateCount = sizeof(dynamicStates) / sizeof(dynamicStates[0]); + dynamicState.pDynamicStates = dynamicStates; VkPipelineShaderStageCreateInfo stages[3] = {}; uint32_t stageCount = 0; - // TODO: Amplification shader + if (parameters->AmplificationShaderFunction) + { + auto shaderStage = GetVulkanShaderFunctionStageCreateInfo(stackMemoryArena, shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); + + if (shaderStage.stage == VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM) + { + return ELEM_HANDLE_NULL; + } + + stages[stageCount++] = shaderStage; + } if (parameters->MeshShaderFunction) { @@ -196,54 +390,17 @@ ElemPipelineState VulkanCompileGraphicsPipelineState(ElemGraphicsDevice graphics stages[stageCount++] = shaderStage; } - + + VkGraphicsPipelineCreateInfo createInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; + createInfo.pColorBlendState = &colorBlendState; + createInfo.pDepthStencilState = &depthStencilState; + createInfo.pNext = &renderingCreateInfo; createInfo.stageCount = stageCount; createInfo.pStages = stages; - - VkPipelineViewportStateCreateInfo viewportState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; - viewportState.viewportCount = 1; - viewportState.scissorCount = 1; createInfo.pViewportState = &viewportState; - - VkPipelineRasterizationStateCreateInfo rasterizationState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; - rasterizationState.lineWidth = 1.0f; - rasterizationState.cullMode = VK_CULL_MODE_NONE;//VK_CULL_MODE_BACK_BIT; - rasterizationState.frontFace = VK_FRONT_FACE_CLOCKWISE; createInfo.pRasterizationState = &rasterizationState; - - VkPipelineMultisampleStateCreateInfo multisampleState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; - multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; createInfo.pMultisampleState = &multisampleState; - - VkPipelineDepthStencilStateCreateInfo depthStencilState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; - createInfo.pDepthStencilState = &depthStencilState; - - // TODO: To Refactor! - VkPipelineColorBlendAttachmentState renderTargetBlendState = { - false, - VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, - VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, - VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT - }; - - VkPipelineColorBlendStateCreateInfo colorBlendState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; - colorBlendState.attachmentCount = 1; - colorBlendState.pAttachments = &renderTargetBlendState; - createInfo.pColorBlendState = &colorBlendState; - - VkDynamicState dynamicStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; - - VkPipelineDynamicStateCreateInfo dynamicState = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO }; - dynamicState.dynamicStateCount = sizeof(dynamicStates) / sizeof(dynamicStates[0]); - dynamicState.pDynamicStates = dynamicStates; createInfo.pDynamicState = &dynamicState; - - VkFormat formats[] = { ConvertToVulkanTextureFormat(parameters->RenderTargets.Items[0].Format) }; // TODO: Fill Correct Back Buffer Format - - VkPipelineRenderingCreateInfo renderingCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; - renderingCreateInfo.colorAttachmentCount = 1; // TODO: Change that - renderingCreateInfo.pColorAttachmentFormats = formats; - createInfo.pNext = &renderingCreateInfo; createInfo.layout = graphicsDeviceData->PipelineLayout; VkPipeline pipelineState; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 06428b0c..0f9c92fc 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -149,7 +149,7 @@ void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t hei swapChainCreateInfo->imageExtent.width = width; swapChainCreateInfo->imageExtent.height = height; - // BUG: The resizing works well but not fullscreen! + // BUG: The resizing works well but not fullscreen! (Windows / Linux?) swapChainData->DeviceObject = CreateVulkanSwapChainObject(swapChainData->CommandQueue, swapChainData->WindowSurface, swapChainCreateInfo, oldSwapChain); for (uint32_t i = 0; i < VULKAN_MAX_SWAPCHAIN_BUFFERS; i++) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index c8015b3c..c7efca69 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -36,6 +36,8 @@ ComPtr dxgiDebugInterface; ComPtr directX12DebugInterface; ComPtr directX12DeviceFactory; +// TODO: For time queries, use https://learn.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12commandqueue-getclockcalibration + void InitDirectX12() { auto stackMemoryArena = SystemGetStackMemoryArena(); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp index 635ecb10..c999d41a 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp @@ -26,6 +26,7 @@ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE ConvertToDirectX12RenderPassBeginningAcc return beginAccessType; } + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE ConvertToDirectX12RenderPassEndingAccessType(ElemRenderPassStoreAction storeAction) { D3D12_RENDER_PASS_ENDING_ACCESS_TYPE endAccessType; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 2ef7a4c1..b57c5279 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -1,5 +1,6 @@ #include "DirectX12Resource.h" #include "DirectX12GraphicsDevice.h" +#include "Graphics/Resource.h" #include "Graphics/ResourceDeleteQueue.h" #include "SystemDataPool.h" #include "SystemFunctions.h" @@ -73,16 +74,6 @@ DXGI_FORMAT ConvertDirectX12FormatWithoutSrgbIfNeeded(DXGI_FORMAT format) } } -bool CheckDirectX12DepthStencilFormat(ElemGraphicsFormat format) -{ - if (format == ElemGraphicsFormat_D32_FLOAT) - { - return true; - } - - return false; -} - ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ComPtr resource, bool isPresentTexture) { InitDirectX12ResourceMemory(); @@ -448,7 +439,7 @@ ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHe return ELEM_HANDLE_NULL; } - if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil && !CheckDirectX12DepthStencilFormat(resourceInfo->Format)) + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil && !CheckDepthStencilFormat(resourceInfo->Format)) { SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage DepthStencil should use a compatible format."); return ELEM_HANDLE_NULL; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 7a69df6c..ce1c52fe 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -2,7 +2,9 @@ #include "DirectX12GraphicsDevice.h" #include "DirectX12Resource.h" #include "DirectX12CommandList.h" +#include "Graphics/Resource.h" #include "Graphics/ShaderReader.h" +#include "Graphics/Shader.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" @@ -94,16 +96,6 @@ D3D12_SHADER_BYTECODE GetDirectX12ShaderFunctionByteCode(DirectX12ShaderLibraryD return result; } -bool IsDirectX12BlendEnabled(ElemGraphicsPipelineStateRenderTarget renderTargetParameters) -{ - return !(renderTargetParameters.BlendOperation == ElemGraphicsBlendOperation_Add && - renderTargetParameters.SourceBlendFactor == ElemGraphicsBlendFactor_Zero && - renderTargetParameters.DestinationBlendFactor == ElemGraphicsBlendFactor_Zero && - renderTargetParameters.BlendOperationAlpha == ElemGraphicsBlendOperation_Add && - renderTargetParameters.SourceBlendFactorAlpha == ElemGraphicsBlendFactor_Zero && - renderTargetParameters.DestinationBlendFactorAlpha == ElemGraphicsBlendFactor_Zero); -} - D3D12_FILL_MODE ConvertToDirectX12FillMode(ElemGraphicsFillMode fillMode) { switch (fillMode) @@ -290,7 +282,7 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev blendState.RenderTarget[i] = { - .BlendEnable = IsDirectX12BlendEnabled(renderTargetParameters), + .BlendEnable = IsBlendEnabled(renderTargetParameters), .SrcBlend = ConvertToDirectX12Blend(renderTargetParameters.SourceBlendFactor), .DestBlend = ConvertToDirectX12Blend(renderTargetParameters.DestinationBlendFactor), .BlendOp = ConvertToDirectX12BlendOperation(renderTargetParameters.BlendOperation), @@ -304,7 +296,7 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev DXGI_FORMAT depthFormat = DXGI_FORMAT_UNKNOWN; D3D12_DEPTH_STENCIL_DESC2 depthStencilState = {}; - if (CheckDirectX12DepthStencilFormat(parameters->DepthStencil.Format)) + if (CheckDepthStencilFormat(parameters->DepthStencil.Format)) { depthFormat = ConvertToDirectX12TextureFormat(parameters->DepthStencil.Format); depthStencilState.DepthEnable = true; @@ -335,9 +327,7 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev rasterizerState.ForcedSampleCount = 0; rasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - D3D12_PIPELINE_STATE_STREAM_DESC psoStream = {}; GraphicsPso psoDesc = {}; - psoDesc.RootSignature = graphicsDeviceData->RootSignature.Get(); if (parameters->AmplificationShaderFunction) @@ -383,6 +373,7 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev psoDesc.BlendState = blendState; psoDesc.SampleDesc = sampleDesc; + D3D12_PIPELINE_STATE_STREAM_DESC psoStream = {}; psoStream.SizeInBytes = sizeof(GraphicsPso); psoStream.pPipelineStateSubobjectStream = &psoDesc; From f7ccf0c7690564f864641961ce6f956bb007dcf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 24 Aug 2024 12:12:43 +0200 Subject: [PATCH 14/93] Update task shader --- .../05-HelloMesh/Data/RenderMesh.hlsl | 69 +++++++++++++++---- samples/Elemental/05-HelloMesh/main.c | 6 +- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl index 3ae7fc3f..f2edeacd 100644 --- a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl @@ -1,3 +1,5 @@ +#define WAVE_SIZE 32 + struct ShaderParameters { uint32_t VertexBufferIndex; @@ -8,6 +10,7 @@ struct ShaderParameters float Zoom; float AspectRatio; uint32_t TriangleColor; + uint32_t MeshletCount; }; [[vk::push_constant]] @@ -21,6 +24,13 @@ struct ElemMeshlet uint32_t TriangleCount; }; +struct MeshShaderPayload +{ + uint MeshletIndexes[WAVE_SIZE]; + float4x4 WorldViewProjectionMatrix; + float4x4 InverseTransposeWorldMatrix; +}; + struct Vertex { float3 Position; @@ -76,17 +86,27 @@ float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) return float4x4(row1, row2, row3, row4); } -[shader("mesh")] -[OutputTopology("triangle")] -[NumThreads(126, 1, 1)] -void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[64], out indices uint3 indices[126]) +groupshared MeshShaderPayload meshShaderPayload; + +[shader("amplification")] +[NumThreads(WAVE_SIZE, 1, 1)] +void AmplificationMain(in uint groupId: SV_GroupID, in uint groupThreadId: SV_GroupThreadID) { - ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; - ElemMeshlet meshlet = meshletBuffer.Load(groupId * sizeof(ElemMeshlet)); + uint meshletIndex = groupId * WAVE_SIZE + groupThreadId; + bool isMeshletVisible = false; - SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); + if (meshletIndex < parameters.MeshletCount) + { + isMeshletVisible = true;//meshletIndex % 2; + } - if (groupThreadId < meshlet.VertexIndexCount) + if (isMeshletVisible) + { + uint laneIndex = WavePrefixCountBits(isMeshletVisible); + meshShaderPayload.MeshletIndexes[laneIndex] = meshletIndex; + } + + if (groupThreadId == 0) { // TODO: Compute matrix in task shader float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; @@ -95,16 +115,40 @@ void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThrea float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); - float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); + meshShaderPayload.WorldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); + meshShaderPayload.InverseTransposeWorldMatrix = worldMatrix; + } + + uint meshletCount = WaveActiveCountBits(isMeshletVisible); + DispatchMesh(meshletCount, 1, 1, meshShaderPayload); +} +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(126, 1, 1)] +void MeshMain(in uint groupId: SV_GroupID, + in uint groupThreadId : SV_GroupThreadID, + in payload MeshShaderPayload payload, + out vertices VertexOutput vertices[64], + out indices uint3 indices[126]) +{ + uint meshletIndex = payload.MeshletIndexes[groupId]; + + ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; + ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); + + SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); + + if (groupThreadId < meshlet.VertexIndexCount) + { ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); - vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); - vertices[groupThreadId].WorldNormal = mul(vertex.Normal, worldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), payload.WorldViewProjectionMatrix); + vertices[groupThreadId].WorldNormal = mul(vertex.Normal, payload.InverseTransposeWorldMatrix); // TODO: Compute inverse transpose vertices[groupThreadId].MeshletIndex = groupId; } @@ -113,8 +157,7 @@ void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThrea ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); - uint32_t4 unpacked = unpack_u8u32(triangleIndex); - indices[groupThreadId] = unpacked.xyz; + indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; } } diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index a32c70ae..c60e80ae 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -24,6 +24,7 @@ typedef struct float Zoom; float AspectRatio; uint32_t TriangeColor; + uint32_t MeshletCount; } ShaderParameters; // TODO: Extract from the gamestate the inputState @@ -131,6 +132,7 @@ void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicat applicationPayload->ShaderParameters.MeshletBuffer = meshData->MeshletBufferReadDescriptor; applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = meshData->MeshletVertexIndexBufferReadDescriptor; applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = meshData->MeshletTriangleIndexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletCount = meshData->MeshletCount; } void InitSample(void* payload) @@ -160,6 +162,7 @@ void InitSample(void* payload) applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { .DebugName = "RenderMesh PSO", .ShaderLibrary = shaderLibrary, + .AmplificationShaderFunction = "AmplificationMain", .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, @@ -366,7 +369,8 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + // TODO: Create a function for the thread group calculation + ElemDispatchMesh(commandList, (applicationPayload->TestMeshData.MeshletCount + (32 - 1)) / 32, 1, 1); ElemEndRenderPass(commandList); From c915608b297edce259e9e92007b1dac2be76dc16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 25 Aug 2024 22:05:03 +0200 Subject: [PATCH 15/93] Implement Apple Metal Depth buffer / Render Pso --- external/CMakeLists.txt | 4 +- samples/Elemental/02-HelloTriangle/main.c | 1 + samples/Elemental/03-HelloInputs/main.c | 1 + .../Apple/Graphics/MetalGraphicsDevice.cpp | 9 +- .../Apple/Graphics/MetalRendering.cpp | 101 +++++--- .../Apple/Graphics/MetalResource.cpp | 28 ++- src/Elemental/Apple/Graphics/MetalShader.cpp | 224 ++++++++++++++---- src/Elemental/Apple/Graphics/MetalShader.h | 5 +- src/Elemental/Elemental.h | 4 +- src/ElementalTools/Common/MeshletBuilder.cpp | 2 +- .../Common/MetalShaderConverter.cpp | 3 +- tests/GraphicsTests/ShaderTests.cpp | 16 +- tests/GraphicsTests/Shaders/Assert.hlsl | 47 ---- 13 files changed, 304 insertions(+), 141 deletions(-) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 3c7ad4c2..d2b50a09 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -206,7 +206,9 @@ endif() #======================================================================= # Mesh Optimizer #======================================================================= -add_subdirectory(./meshoptimizer) +if(NOT BUILD_FOR_IOS) + add_subdirectory(./meshoptimizer) +endif() #======================================================================= # Tools External Dependencies diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c index 501933d0..bad3d470 100644 --- a/samples/Elemental/02-HelloTriangle/main.c +++ b/samples/Elemental/02-HelloTriangle/main.c @@ -41,6 +41,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", + .CullMode = ElemGraphicsCullMode_None, .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, }); diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 95933dd2..72d5fb2c 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -182,6 +182,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", + .CullMode = ElemGraphicsCullMode_None, .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, }); diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp index 951b5e12..c37b7d45 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp @@ -50,9 +50,16 @@ void* MetalDebugReportCallback(void* arg) { auto message = ((ReadOnlySpan)splittedStrings[1]).Slice(1); + // TODO: Refactor if (SystemFindSubString(message, "Metal API Validation Enabled") == -1) { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, message); + if (SystemFindSubString(message, "unused binding in encoder") == -1) + { + if (SystemFindSubString(message, "redundant setting") == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, message); + } + } } } } diff --git a/src/Elemental/Apple/Graphics/MetalRendering.cpp b/src/Elemental/Apple/Graphics/MetalRendering.cpp index 97ec5157..aa9b4808 100644 --- a/src/Elemental/Apple/Graphics/MetalRendering.cpp +++ b/src/Elemental/Apple/Graphics/MetalRendering.cpp @@ -6,6 +6,46 @@ #include "SystemFunctions.h" #include "SystemLogging.h" +MTL::LoadAction ConvertToMetalLoadAction(ElemRenderPassLoadAction loadAction) +{ + MTL::LoadAction metalLoadAction; + + switch (loadAction) + { + case ElemRenderPassLoadAction_Load: + metalLoadAction = MTL::LoadActionLoad; + break; + + case ElemRenderPassLoadAction_Clear: + metalLoadAction = MTL::LoadActionClear; + break; + + default: + metalLoadAction = MTL::LoadActionDontCare; + break; + } + + return metalLoadAction; +} + +MTL::StoreAction ConvertToMetalStoreAction(ElemRenderPassStoreAction storeAction) +{ + MTL::StoreAction metalStoreAction; + + switch (storeAction) + { + case ElemRenderPassStoreAction_Store: + metalStoreAction = MTL::StoreActionStore; + break; + + default: + metalStoreAction = MTL::StoreActionDontCare; + break; + } + + return metalStoreAction; + +} void MetalBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPassParameters* parameters) { // TODO: Check command list type != COMPUTE @@ -34,35 +74,8 @@ void MetalBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPass auto renderTargetDescriptor = renderPassDescriptor->colorAttachments()->object(i); - MTL::LoadAction loadAction; - - switch (renderTargetParameters.LoadAction) - { - case ElemRenderPassLoadAction_Load: - loadAction = MTL::LoadActionLoad; - break; - - case ElemRenderPassLoadAction_Clear: - loadAction = MTL::LoadActionClear; - break; - - default: - loadAction = MTL::LoadActionDontCare; - break; - } - - MTL::StoreAction storeAction; - - switch (renderTargetParameters.StoreAction) - { - case ElemRenderPassStoreAction_Store: - storeAction = MTL::StoreActionStore; - break; - - default: - storeAction = MTL::StoreActionDontCare; - break; - } + auto loadAction = ConvertToMetalLoadAction(renderTargetParameters.LoadAction); + auto storeAction = ConvertToMetalStoreAction(renderTargetParameters.StoreAction); if (textureData->DeviceObject.get() == nullptr) { @@ -80,6 +93,24 @@ void MetalBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPass } } + if (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) + { + auto depthStencilParameters = parameters->DepthStencil; + + auto textureData = GetMetalResourceData(depthStencilParameters.DepthStencil); + SystemAssert(textureData); + + auto depthDescriptor = renderPassDescriptor->depthAttachment(); + // TODO: Validate usage + auto depthLoadAction = ConvertToMetalLoadAction(depthStencilParameters.DepthLoadAction); + auto depthStoreAction = ConvertToMetalStoreAction(depthStencilParameters.DepthStoreAction); + + depthDescriptor->setTexture((MTL::Texture*)textureData->DeviceObject.get()); + depthDescriptor->setLoadAction(depthLoadAction); + depthDescriptor->setStoreAction(depthStoreAction); + depthDescriptor->setClearDepth(depthStencilParameters.DepthClearValue); + } + auto renderCommandEncoder = NS::RetainPtr(commandListData->DeviceObject->renderCommandEncoder(renderPassDescriptor.get())); if (!renderCommandEncoder.get()) @@ -186,9 +217,17 @@ void MetalDispatchMesh(ElemCommandList commandList, uint32_t threadGroupCountX, auto pipelineStateData = GetMetalPipelineStateData(commandListData->PipelineState); SystemAssert(pipelineStateData); - // TODO: Get the correct threads config for amplification shader + auto amplificationShaderThreadSize = MTL::Size(1, 1, 1); + + if (pipelineStateData->AmplificationShaderMetaData.ThreadSizeX > 0) + { + amplificationShaderThreadSize = MTL::Size(pipelineStateData->AmplificationShaderMetaData.ThreadSizeX, + pipelineStateData->AmplificationShaderMetaData.ThreadSizeY, + pipelineStateData->AmplificationShaderMetaData.ThreadSizeZ); + } + auto renderCommandEncoder = (MTL::RenderCommandEncoder*)commandListData->CommandEncoder.get(); renderCommandEncoder->drawMeshThreadgroups(MTL::Size(threadGroupCountX, threadGroupCountY, threadGroupCountZ), - MTL::Size(32, 1, 1), + amplificationShaderThreadSize, MTL::Size(pipelineStateData->MeshShaderMetaData.ThreadSizeX, pipelineStateData->MeshShaderMetaData.ThreadSizeY, pipelineStateData->MeshShaderMetaData.ThreadSizeZ)); } diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index e3caa77d..6c042947 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -2,6 +2,7 @@ #include "MetalGraphicsDevice.h" #include "MetalCommandList.h" #include "Graphics/ResourceDeleteQueue.h" +#include "Graphics/Resource.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" @@ -60,6 +61,9 @@ MTL::PixelFormat ConvertToMetalResourceFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_R32G32B32A32_FLOAT: return MTL::PixelFormatRGBA32Float; + case ElemGraphicsFormat_D32_FLOAT: + return MTL::PixelFormatDepth32Float; + default: return MTL::PixelFormatR8Unorm; } @@ -81,6 +85,9 @@ ElemGraphicsFormat ConvertFromMetalResourceFormat(MTL::PixelFormat format) case MTL::PixelFormatRGBA32Float: return ElemGraphicsFormat_R32G32B32A32_FLOAT; + case MTL::PixelFormatDepth32Float: + return ElemGraphicsFormat_D32_FLOAT; + default: return ElemGraphicsFormat_Raw; } @@ -95,7 +102,8 @@ MTL::TextureUsage ConvertToMetalResourceUsage(ElemGraphicsResourceUsage usage) result |= MTL::TextureUsageShaderWrite; } - if (usage & ElemGraphicsResourceUsage_RenderTarget) + if (usage & ElemGraphicsResourceUsage_RenderTarget || + usage & ElemGraphicsResourceUsage_DepthStencil) { result |= MTL::TextureUsageRenderTarget; } @@ -318,6 +326,18 @@ ElemGraphicsResource MetalCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, return ELEM_HANDLE_NULL; } + if ((resourceInfo->Usage & ElemGraphicsResourceUsage_RenderTarget) && (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage RenderTarget and DepthStencil should not be used together."); + return ELEM_HANDLE_NULL; + } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil && !CheckDepthStencilFormat(resourceInfo->Format)) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Texture2D with usage DepthStencil should use a compatible format."); + return ELEM_HANDLE_NULL; + } + auto textureDescriptor = CreateMetalTextureDescriptor(resourceInfo); resource = NS::TransferPtr(graphicsHeapData->DeviceObject->newTexture(textureDescriptor.get(), graphicsHeapOffset)); } @@ -334,6 +354,12 @@ ElemGraphicsResource MetalCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to RenderTarget."); return ELEM_HANDLE_NULL; } + + if (resourceInfo->Usage & ElemGraphicsResourceUsage_DepthStencil) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GraphicsBuffer usage should not be equals to DepthStencil."); + return ELEM_HANDLE_NULL; + } resource = NS::TransferPtr(graphicsHeapData->DeviceObject->newBuffer(resourceInfo->Width, MTL::ResourceHazardTrackingModeUntracked, graphicsHeapOffset)); } diff --git a/src/Elemental/Apple/Graphics/MetalShader.cpp b/src/Elemental/Apple/Graphics/MetalShader.cpp index 47fd453c..9f19c8dd 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.cpp +++ b/src/Elemental/Apple/Graphics/MetalShader.cpp @@ -3,6 +3,8 @@ #include "MetalCommandList.h" #include "MetalResource.h" #include "MetalResourceBarrier.h" +#include "Graphics/Resource.h" +#include "Graphics/Shader.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemLogging.h" @@ -100,6 +102,8 @@ MetalShaderFunctionData GetMetalShaderFunction(MetalShaderLibraryData* shaderLib result.MetaData.ThreadSizeZ = metaData.Value[2]; } } + + break; } } @@ -111,6 +115,111 @@ MetalShaderFunctionData GetMetalShaderFunction(MetalShaderLibraryData* shaderLib return result; } +MTL::CullMode ConvertToMetalCullMode(ElemGraphicsCullMode cullMode) +{ + switch (cullMode) + { + case ElemGraphicsCullMode_BackFace: + return MTL::CullModeBack; + + case ElemGraphicsCullMode_FrontFace: + return MTL::CullModeFront; + + case ElemGraphicsCullMode_None: + return MTL::CullModeNone; + } +} + +MTL::BlendOperation ConvertToMetalBlendOperation(ElemGraphicsBlendOperation blendOperation) +{ + switch (blendOperation) + { + case ElemGraphicsBlendOperation_Add: + return MTL::BlendOperationAdd; + + case ElemGraphicsBlendOperation_Subtract: + return MTL::BlendOperationSubtract; + + case ElemGraphicsBlendOperation_ReverseSubtract: + return MTL::BlendOperationReverseSubtract; + + case ElemGraphicsBlendOperation_Min: + return MTL::BlendOperationMin; + + case ElemGraphicsBlendOperation_Max: + return MTL::BlendOperationMax; + } +} + +MTL::BlendFactor ConvertToMetalBlendFactor(ElemGraphicsBlendFactor blendFactor) +{ + switch (blendFactor) + { + case ElemGraphicsBlendFactor_Zero: + return MTL::BlendFactorZero; + + case ElemGraphicsBlendFactor_One: + return MTL::BlendFactorOne; + + case ElemGraphicsBlendFactor_SourceColor: + return MTL::BlendFactorSourceColor; + + case ElemGraphicsBlendFactor_InverseSourceColor: + return MTL::BlendFactorOneMinusSourceColor; + + case ElemGraphicsBlendFactor_SourceAlpha: + return MTL::BlendFactorSourceAlpha; + + case ElemGraphicsBlendFactor_InverseSourceAlpha: + return MTL::BlendFactorOneMinusSourceAlpha; + + case ElemGraphicsBlendFactor_DestinationColor: + return MTL::BlendFactorDestinationColor; + + case ElemGraphicsBlendFactor_InverseDestinationColor: + return MTL::BlendFactorOneMinusDestinationColor; + + case ElemGraphicsBlendFactor_DestinationAlpha: + return MTL::BlendFactorDestinationAlpha; + + case ElemGraphicsBlendFactor_InverseDestinationAlpha: + return MTL::BlendFactorOneMinusDestinationAlpha; + + case ElemGraphicsBlendFactor_SourceAlphaSaturated: + return MTL::BlendFactorSourceAlphaSaturated; + } +} + +MTL::CompareFunction ConvertToMetalCompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return MTL::CompareFunctionNever; + + case ElemGraphicsCompareFunction_Less: + return MTL::CompareFunctionLess; + + case ElemGraphicsCompareFunction_Equal: + return MTL::CompareFunctionEqual; + + case ElemGraphicsCompareFunction_LessEqual: + return MTL::CompareFunctionLessEqual; + + case ElemGraphicsCompareFunction_Greater: + return MTL::CompareFunctionGreater; + + case ElemGraphicsCompareFunction_NotEqual: + return MTL::CompareFunctionNotEqual; + + case ElemGraphicsCompareFunction_GreaterEqual: + return MTL::CompareFunctionGreaterEqual; + + case ElemGraphicsCompareFunction_Always: + return MTL::CompareFunctionAlways; + } +} + ElemShaderLibrary MetalCreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) { InitMetalShaderLibraryMemory(); @@ -203,9 +312,62 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD SystemAssert(shaderLibraryData); auto pipelineStateDescriptor = NS::TransferPtr(MTL::MeshRenderPipelineDescriptor::alloc()->init()); + + for (uint32_t i = 0; i < parameters->RenderTargets.Length; i++) + { + auto renderTargetParameters = parameters->RenderTargets.Items[i]; + auto metalColorAttachment = pipelineStateDescriptor->colorAttachments()->object(i); + metalColorAttachment->setPixelFormat(ConvertToMetalResourceFormat(renderTargetParameters.Format)); + + metalColorAttachment->setBlendingEnabled(IsBlendEnabled(renderTargetParameters)); + metalColorAttachment->setSourceRGBBlendFactor(ConvertToMetalBlendFactor(renderTargetParameters.SourceBlendFactor)); + metalColorAttachment->setDestinationRGBBlendFactor(ConvertToMetalBlendFactor(renderTargetParameters.DestinationBlendFactor)); + metalColorAttachment->setRgbBlendOperation(ConvertToMetalBlendOperation(renderTargetParameters.BlendOperation)); + metalColorAttachment->setSourceAlphaBlendFactor(ConvertToMetalBlendFactor(renderTargetParameters.SourceBlendFactorAlpha)); + metalColorAttachment->setDestinationAlphaBlendFactor(ConvertToMetalBlendFactor(renderTargetParameters.DestinationBlendFactorAlpha)); + metalColorAttachment->setAlphaBlendOperation(ConvertToMetalBlendOperation(renderTargetParameters.BlendOperationAlpha)); + metalColorAttachment->setWriteMask(MTL::ColorWriteMaskAll); + } + + NS::SharedPtr depthStencilState = {}; + + if (CheckDepthStencilFormat(parameters->DepthStencil.Format)) + { + pipelineStateDescriptor->setDepthAttachmentPixelFormat(ConvertToMetalResourceFormat(parameters->DepthStencil.Format)); + + auto depthStencilDescriptor = NS::TransferPtr(MTL::DepthStencilDescriptor::alloc()->init()); + + if (!parameters->DepthStencil.DepthDisableWrite) + { + depthStencilDescriptor->setDepthWriteEnabled(true); + } + + depthStencilDescriptor->setDepthCompareFunction(ConvertToMetalCompareFunction(parameters->DepthStencil.DepthCompareFunction)); + depthStencilState = NS::TransferPtr(graphicsDeviceData->Device->newDepthStencilState(depthStencilDescriptor.get())); + } + + auto cullMode = ConvertToMetalCullMode(parameters->CullMode); + auto fillMode = parameters->FillMode == ElemGraphicsFillMode_Solid ? MTL::TriangleFillModeFill : MTL::TriangleFillModeLines; + + MetalShaderMetaData amplificationShaderMetaData = {}; MetalShaderMetaData meshShaderMetaData = {}; + if (parameters->AmplificationShaderFunction) + { + auto functionData = GetMetalShaderFunction(shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); + + if (!functionData.Function) + { + return ELEM_HANDLE_NULL; + } + + pipelineStateDescriptor->setObjectFunction(functionData.Function.get()); + amplificationShaderMetaData.ThreadSizeX = functionData.MetaData.ThreadSizeX; + amplificationShaderMetaData.ThreadSizeY = functionData.MetaData.ThreadSizeY; + amplificationShaderMetaData.ThreadSizeZ = functionData.MetaData.ThreadSizeZ; + } + if (parameters->MeshShaderFunction) { auto functionData = GetMetalShaderFunction(shaderLibraryData, ShaderType_Mesh, parameters->MeshShaderFunction); @@ -232,53 +394,6 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD pipelineStateDescriptor->setFragmentFunction(functionData.Function.get()); } - - for (uint32_t i = 0; i < parameters->TextureFormats.Length; i++) - { - pipelineStateDescriptor->colorAttachments()->object(0)->setPixelFormat(ConvertToMetalResourceFormat(parameters->TextureFormats.Items[i])); - } - - // TODO: Why is the triangle not back face culled by default? - //pipelineStateDescriptor.supportIndirectCommandBuffers = true - - // TODO: Use the correct render target format - /*if (renderPassDescriptor->RenderTarget0.HasValue) - { - pipelineStateDescriptor->colorAttachments()->object(0)->setPixelFormat(MTL::PixelFormatBGRA8Unorm_sRGB); - }*/ - - /* - if (metalRenderPassDescriptor.RenderTarget2TextureFormat.HasValue == 1) { - pipelineStateDescriptor.colorAttachments[1].pixelFormat = convertTextureFormat(metalRenderPassDescriptor.RenderTarget2TextureFormat.Value) - } - - if (metalRenderPassDescriptor.RenderTarget3TextureFormat.HasValue == 1) { - pipelineStateDescriptor.colorAttachments[2].pixelFormat = convertTextureFormat(metalRenderPassDescriptor.RenderTarget3TextureFormat.Value) - } - - if (metalRenderPassDescriptor.RenderTarget4TextureFormat.HasValue == 1) { - pipelineStateDescriptor.colorAttachments[3].pixelFormat = convertTextureFormat(metalRenderPassDescriptor.RenderTarget4TextureFormat.Value) - } - - if (metalRenderPassDescriptor.DepthTexturePointer.HasValue == 1) { - pipelineStateDescriptor.depthAttachmentPixelFormat = .depth32Float - } -) - if (metalRenderPassDescriptor.RenderTarget1BlendOperation.HasValue == 1) { - initBlendState(pipelineStateDescriptor.colorAttachments[0]!, metalRenderPassDescriptor.RenderTarget1BlendOperation.Value) - } - - if (metalRenderPassDescriptor.RenderTarget2BlendOperation.HasValue == 1) { - initBlendState(pipelineStateDescriptor.colorAttachments[1]!, metalRenderPassDescriptor.RenderTarget2BlendOperation.Value) - } - - if (metalRenderPassDescriptor.RenderTarget3BlendOperation.HasValue == 1) { - initBlendState(pipelineStateDescriptor.colorAttachments[2]!, metalRenderPassDescriptor.RenderTarget3BlendOperation.Value) - } - - if (metalRenderPassDescriptor.RenderTarget4BlendOperation.HasValue == 1) { - initBlendState(pipelineStateDescriptor.colorAttachments[3]!, metalRenderPassDescriptor.RenderTarget4BlendOperation.Value) - }*/ // TODO: Review this! auto dispatchGroup = dispatch_group_create(); @@ -305,6 +420,10 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD auto handle = SystemAddDataPoolItem(metalPipelineStatePool, { .RenderPipelineState = pipelineState, + .RenderDepthStencilState = depthStencilState, + .RenderCullMode = cullMode, + .RenderFillMode = fillMode, + .AmplificationShaderMetaData = amplificationShaderMetaData, .MeshShaderMetaData = meshShaderMetaData }); @@ -405,6 +524,14 @@ void MetalBindPipelineState(ElemCommandList commandList, ElemPipelineState pipel { auto renderCommandEncoder = (MTL::RenderCommandEncoder*)commandListData->CommandEncoder.get(); renderCommandEncoder->setRenderPipelineState(pipelineStateData->RenderPipelineState.get()); + + if (pipelineStateData->RenderDepthStencilState) + { + renderCommandEncoder->setDepthStencilState(pipelineStateData->RenderDepthStencilState.get()); + } + + renderCommandEncoder->setCullMode(pipelineStateData->RenderCullMode); + renderCommandEncoder->setTriangleFillMode(pipelineStateData->RenderFillMode); } } else @@ -448,6 +575,9 @@ void MetalPushPipelineStateConstants(ElemCommandList commandList, uint32_t offse // TODO: compute offset // HACK: For the oment we set the slot 2 because it is the global one for bindless + renderCommandEncoder->setObjectBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + renderCommandEncoder->setObjectBytes(data.Items, data.Length, 2); + renderCommandEncoder->setMeshBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); renderCommandEncoder->setMeshBytes(data.Items, data.Length, 2); diff --git a/src/Elemental/Apple/Graphics/MetalShader.h b/src/Elemental/Apple/Graphics/MetalShader.h index 7bf1d24f..b537cfaf 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.h +++ b/src/Elemental/Apple/Graphics/MetalShader.h @@ -22,7 +22,10 @@ struct MetalPipelineStateData { NS::SharedPtr RenderPipelineState; NS::SharedPtr ComputePipelineState; - MetalShaderMetaData AmplificaitonShaderMetaData; + NS::SharedPtr RenderDepthStencilState; + MTL::CullMode RenderCullMode; + MTL::TriangleFillMode RenderFillMode; + MetalShaderMetaData AmplificationShaderMetaData; MetalShaderMetaData MeshShaderMetaData; MetalShaderMetaData ComputeShaderMetaData; }; diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 64b97a90..f87abf8d 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -419,8 +419,8 @@ typedef enum typedef enum { ElemGraphicsBlendOperation_Add = 0, - ElemGraphicsBlendOperation_Substract = 1, - ElemGraphicsBlendOperation_ReverseSubstract = 2, + ElemGraphicsBlendOperation_Subtract = 1, + ElemGraphicsBlendOperation_ReverseSubtract = 2, ElemGraphicsBlendOperation_Min = 3, ElemGraphicsBlendOperation_Max = 4, } ElemGraphicsBlendOperation; diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp index 729ee687..e15eed26 100644 --- a/src/ElementalTools/Common/MeshletBuilder.cpp +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -96,7 +96,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf } meshletVertexIndexCount += meshlet.vertex_count; - meshletTriangleIndexCount = max(meshletTriangleIndexCount, meshlet.triangle_offset / 3 + meshlet.triangle_count); + meshletTriangleIndexCount = fmax(meshletTriangleIndexCount, meshlet.triangle_offset / 3 + meshlet.triangle_count); } return diff --git a/src/ElementalTools/Common/MetalShaderConverter.cpp b/src/ElementalTools/Common/MetalShaderConverter.cpp index d3277c0f..6a60f654 100644 --- a/src/ElementalTools/Common/MetalShaderConverter.cpp +++ b/src/ElementalTools/Common/MetalShaderConverter.cpp @@ -102,7 +102,7 @@ ElemShaderCompilationResult MetalShaderConverterCompileShader(MemoryArena memory } else { - IRCompilerSetMinimumDeploymentTarget(pCompiler, IROperatingSystem_macOS, "14.0.0"); + IRCompilerSetMinimumDeploymentTarget(pCompiler, IROperatingSystem_macOS, "15.0.0"); } auto compilationMessages = SystemPushArray(memoryArena, 1024); @@ -188,6 +188,7 @@ ElemShaderCompilationResult MetalShaderConverterCompileShader(MemoryArena memory if (!result) { // TODO: Error + printf("ERRORRRRRR!\n"); } size_t metallibSize = IRMetalLibGetBytecodeSize(pMetallib); diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index 76c81941..e5ec010b 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -938,9 +938,9 @@ UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Add_SourceAlphaSaturated_ utest_fixture->ResultColor[3] = 0.0f; } -UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Substract_One_One) +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Subtract_One_One) { - utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Substract; + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Subtract; utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; @@ -964,9 +964,9 @@ UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Substract_One_One) utest_fixture->ResultColor[3] = 0.0f; } -UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, ReverseSubstract_One_One) +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, ReverseSubtract_One_One) { - utest_fixture->BlendOperation = ElemGraphicsBlendOperation_ReverseSubstract; + utest_fixture->BlendOperation = ElemGraphicsBlendOperation_ReverseSubtract; utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_One; utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_One; @@ -1042,13 +1042,13 @@ UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, Max_One_One) utest_fixture->ResultColor[3] = 0.0f; } -UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaSubstract_One_One) +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaSubtract_One_One) { utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; - utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Substract; + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_Subtract; utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; @@ -1068,13 +1068,13 @@ UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaSubstract_One_One) utest_fixture->ResultColor[3] = 0.5f; } -UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaReverseSubstract_One_One) +UTEST_F(Shader_CompileGraphicsPipelineStateBlendState, AlphaReverseSubtract_One_One) { utest_fixture->BlendOperation = ElemGraphicsBlendOperation_Add; utest_fixture->SourceBlendFactor = ElemGraphicsBlendFactor_Zero; utest_fixture->DestinationBlendFactor = ElemGraphicsBlendFactor_Zero; - utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_ReverseSubstract; + utest_fixture->BlendOperationAlpha = ElemGraphicsBlendOperation_ReverseSubtract; utest_fixture->SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One; utest_fixture->DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One; diff --git a/tests/GraphicsTests/Shaders/Assert.hlsl b/tests/GraphicsTests/Shaders/Assert.hlsl index 47323ba6..86c3027a 100644 --- a/tests/GraphicsTests/Shaders/Assert.hlsl +++ b/tests/GraphicsTests/Shaders/Assert.hlsl @@ -1,4 +1,3 @@ -// TODO: Move copy texture to assert.hlsl and rename struct Parameters { uint SourceBufferIndex; @@ -27,49 +26,3 @@ void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); } - -struct VertexOutput -{ - float4 Position: SV_Position; -}; - -static float3 rectangleVertices[] = -{ - float3(-1.0, 1.0, 0.0), - float3(1.0, 1.0, 0.0), - float3(-1.0, -1.0, 0.0), - float3(1.0, -1.0, 0.0) -}; - -static uint3 rectanglePrimitives[] = -{ - uint3(0, 1, 2), - uint3(2, 1, 3) -}; - -[shader("mesh")] -[OutputTopology("triangle")] -[NumThreads(4, 1, 1)] -void MeshShader(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[4], out indices uint3 indices[2]) -{ - const uint meshVertexCount = 4; - const uint meshPrimitiveCount = 2; - - SetMeshOutputCounts(meshVertexCount, meshPrimitiveCount); - - if (groupThreadId < meshVertexCount) - { - vertices[groupThreadId].Position = float4(rectangleVertices[groupThreadId], 1); - } - - if (groupThreadId < meshPrimitiveCount) - { - indices[groupThreadId] = rectanglePrimitives[groupThreadId]; - } -} - -[shader("pixel")] -float4 PixelShader(const VertexOutput input) : SV_Target0 -{ - return float4(1.0, 1.0, 0.0, 1.0); -} From 732f43688c9ea08353dd5a8de45f91c6b1e8a3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 26 Aug 2024 05:49:03 +0200 Subject: [PATCH 16/93] Fix build error --- src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp | 4 ++-- src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 8cb36e75..0b169eab 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -119,10 +119,10 @@ VkBlendOp ConvertToVulkanBlendOperation(ElemGraphicsBlendOperation blendOperatio case ElemGraphicsBlendOperation_Add: return VK_BLEND_OP_ADD; - case ElemGraphicsBlendOperation_Substract: + case ElemGraphicsBlendOperation_Subtract: return VK_BLEND_OP_SUBTRACT; - case ElemGraphicsBlendOperation_ReverseSubstract: + case ElemGraphicsBlendOperation_ReverseSubtract: return VK_BLEND_OP_REVERSE_SUBTRACT; case ElemGraphicsBlendOperation_Min: diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index ce1c52fe..a0850976 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -130,10 +130,10 @@ D3D12_BLEND_OP ConvertToDirectX12BlendOperation(ElemGraphicsBlendOperation blend case ElemGraphicsBlendOperation_Add: return D3D12_BLEND_OP_ADD; - case ElemGraphicsBlendOperation_Substract: + case ElemGraphicsBlendOperation_Subtract: return D3D12_BLEND_OP_SUBTRACT; - case ElemGraphicsBlendOperation_ReverseSubstract: + case ElemGraphicsBlendOperation_ReverseSubtract: return D3D12_BLEND_OP_REV_SUBTRACT; case ElemGraphicsBlendOperation_Min: From a19f5f5757ff8ac979059790c48b3ff8ba764eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 26 Aug 2024 13:35:21 +0200 Subject: [PATCH 17/93] Remove amplification shaders --- .../05-HelloMesh/Data/RenderMesh.hlsl | 63 ++------- samples/Elemental/05-HelloMesh/main.c | 4 +- .../Apple/Graphics/MetalRendering.cpp | 11 +- src/Elemental/Apple/Graphics/MetalShader.cpp | 20 --- src/Elemental/Apple/Graphics/MetalShader.h | 1 - src/Elemental/Common/Graphics/ShaderReader.h | 1 - src/Elemental/Elemental.h | 1 - .../Common/DirectXShaderCompiler.cpp | 6 - .../Common/MetalShaderConverter.cpp | 3 - .../Common/ShaderCompilerUtils.h | 1 - tests/GraphicsTests/GraphicsTests.cpp | 8 +- tests/GraphicsTests/GraphicsTests.h | 1 - tests/GraphicsTests/RenderingTests.cpp | 126 ------------------ 13 files changed, 16 insertions(+), 230 deletions(-) diff --git a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl index f2edeacd..819a0583 100644 --- a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl @@ -1,5 +1,3 @@ -#define WAVE_SIZE 32 - struct ShaderParameters { uint32_t VertexBufferIndex; @@ -24,13 +22,6 @@ struct ElemMeshlet uint32_t TriangleCount; }; -struct MeshShaderPayload -{ - uint MeshletIndexes[WAVE_SIZE]; - float4x4 WorldViewProjectionMatrix; - float4x4 InverseTransposeWorldMatrix; -}; - struct Vertex { float3 Position; @@ -86,53 +77,15 @@ float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) return float4x4(row1, row2, row3, row4); } -groupshared MeshShaderPayload meshShaderPayload; - -[shader("amplification")] -[NumThreads(WAVE_SIZE, 1, 1)] -void AmplificationMain(in uint groupId: SV_GroupID, in uint groupThreadId: SV_GroupThreadID) -{ - uint meshletIndex = groupId * WAVE_SIZE + groupThreadId; - bool isMeshletVisible = false; - - if (meshletIndex < parameters.MeshletCount) - { - isMeshletVisible = true;//meshletIndex % 2; - } - - if (isMeshletVisible) - { - uint laneIndex = WavePrefixCountBits(isMeshletVisible); - meshShaderPayload.MeshletIndexes[laneIndex] = meshletIndex; - } - - if (groupThreadId == 0) - { - // TODO: Compute matrix in task shader - float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; - - float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); - float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); - float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); - - meshShaderPayload.WorldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); - meshShaderPayload.InverseTransposeWorldMatrix = worldMatrix; - } - - uint meshletCount = WaveActiveCountBits(isMeshletVisible); - DispatchMesh(meshletCount, 1, 1, meshShaderPayload); -} - [shader("mesh")] [OutputTopology("triangle")] [NumThreads(126, 1, 1)] void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThreadID, - in payload MeshShaderPayload payload, out vertices VertexOutput vertices[64], out indices uint3 indices[126]) { - uint meshletIndex = payload.MeshletIndexes[groupId]; + uint meshletIndex = groupId; ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); @@ -141,14 +94,24 @@ void MeshMain(in uint groupId: SV_GroupID, if (groupThreadId < meshlet.VertexIndexCount) { + // TODO: Compute matrix in task shader + float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; + + float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); + float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); + float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); + + float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); + float4x4 inverseTransposeWorldMatrix = worldMatrix; + ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); - vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), payload.WorldViewProjectionMatrix); - vertices[groupThreadId].WorldNormal = mul(vertex.Normal, payload.InverseTransposeWorldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); + vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose vertices[groupThreadId].MeshletIndex = groupId; } diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/05-HelloMesh/main.c index c60e80ae..a681e2cb 100644 --- a/samples/Elemental/05-HelloMesh/main.c +++ b/samples/Elemental/05-HelloMesh/main.c @@ -162,7 +162,6 @@ void InitSample(void* payload) applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { .DebugName = "RenderMesh PSO", .ShaderLibrary = shaderLibrary, - .AmplificationShaderFunction = "AmplificationMain", .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, @@ -369,8 +368,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - // TODO: Create a function for the thread group calculation - ElemDispatchMesh(commandList, (applicationPayload->TestMeshData.MeshletCount + (32 - 1)) / 32, 1, 1); + ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); ElemEndRenderPass(commandList); diff --git a/src/Elemental/Apple/Graphics/MetalRendering.cpp b/src/Elemental/Apple/Graphics/MetalRendering.cpp index aa9b4808..8f3fb82f 100644 --- a/src/Elemental/Apple/Graphics/MetalRendering.cpp +++ b/src/Elemental/Apple/Graphics/MetalRendering.cpp @@ -217,17 +217,8 @@ void MetalDispatchMesh(ElemCommandList commandList, uint32_t threadGroupCountX, auto pipelineStateData = GetMetalPipelineStateData(commandListData->PipelineState); SystemAssert(pipelineStateData); - auto amplificationShaderThreadSize = MTL::Size(1, 1, 1); - - if (pipelineStateData->AmplificationShaderMetaData.ThreadSizeX > 0) - { - amplificationShaderThreadSize = MTL::Size(pipelineStateData->AmplificationShaderMetaData.ThreadSizeX, - pipelineStateData->AmplificationShaderMetaData.ThreadSizeY, - pipelineStateData->AmplificationShaderMetaData.ThreadSizeZ); - } - auto renderCommandEncoder = (MTL::RenderCommandEncoder*)commandListData->CommandEncoder.get(); renderCommandEncoder->drawMeshThreadgroups(MTL::Size(threadGroupCountX, threadGroupCountY, threadGroupCountZ), - amplificationShaderThreadSize, + MTL::Size(1, 1, 1), MTL::Size(pipelineStateData->MeshShaderMetaData.ThreadSizeX, pipelineStateData->MeshShaderMetaData.ThreadSizeY, pipelineStateData->MeshShaderMetaData.ThreadSizeZ)); } diff --git a/src/Elemental/Apple/Graphics/MetalShader.cpp b/src/Elemental/Apple/Graphics/MetalShader.cpp index 9f19c8dd..bfcd23ff 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.cpp +++ b/src/Elemental/Apple/Graphics/MetalShader.cpp @@ -353,21 +353,6 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD MetalShaderMetaData amplificationShaderMetaData = {}; MetalShaderMetaData meshShaderMetaData = {}; - if (parameters->AmplificationShaderFunction) - { - auto functionData = GetMetalShaderFunction(shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); - - if (!functionData.Function) - { - return ELEM_HANDLE_NULL; - } - - pipelineStateDescriptor->setObjectFunction(functionData.Function.get()); - amplificationShaderMetaData.ThreadSizeX = functionData.MetaData.ThreadSizeX; - amplificationShaderMetaData.ThreadSizeY = functionData.MetaData.ThreadSizeY; - amplificationShaderMetaData.ThreadSizeZ = functionData.MetaData.ThreadSizeZ; - } - if (parameters->MeshShaderFunction) { auto functionData = GetMetalShaderFunction(shaderLibraryData, ShaderType_Mesh, parameters->MeshShaderFunction); @@ -423,7 +408,6 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD .RenderDepthStencilState = depthStencilState, .RenderCullMode = cullMode, .RenderFillMode = fillMode, - .AmplificationShaderMetaData = amplificationShaderMetaData, .MeshShaderMetaData = meshShaderMetaData }); @@ -570,14 +554,10 @@ void MetalPushPipelineStateConstants(ElemCommandList commandList, uint32_t offse { auto renderCommandEncoder = (MTL::RenderCommandEncoder*)commandListData->CommandEncoder.get(); - // TODO: Amplification shader // TODO: Do we need to check if the shader stage is bound? // TODO: compute offset // HACK: For the oment we set the slot 2 because it is the global one for bindless - renderCommandEncoder->setObjectBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); - renderCommandEncoder->setObjectBytes(data.Items, data.Length, 2); - renderCommandEncoder->setMeshBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); renderCommandEncoder->setMeshBytes(data.Items, data.Length, 2); diff --git a/src/Elemental/Apple/Graphics/MetalShader.h b/src/Elemental/Apple/Graphics/MetalShader.h index b537cfaf..99677e36 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.h +++ b/src/Elemental/Apple/Graphics/MetalShader.h @@ -25,7 +25,6 @@ struct MetalPipelineStateData NS::SharedPtr RenderDepthStencilState; MTL::CullMode RenderCullMode; MTL::TriangleFillMode RenderFillMode; - MetalShaderMetaData AmplificationShaderMetaData; MetalShaderMetaData MeshShaderMetaData; MetalShaderMetaData ComputeShaderMetaData; }; diff --git a/src/Elemental/Common/Graphics/ShaderReader.h b/src/Elemental/Common/Graphics/ShaderReader.h index cbd24461..29bacf6c 100644 --- a/src/Elemental/Common/Graphics/ShaderReader.h +++ b/src/Elemental/Common/Graphics/ShaderReader.h @@ -5,7 +5,6 @@ enum ShaderType { ShaderType_Unknown = 0, - ShaderType_Amplification = 1, ShaderType_Mesh = 2, ShaderType_Pixel = 3, ShaderType_Compute = 4, diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index f87abf8d..fbeceadf 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -752,7 +752,6 @@ typedef struct { // Shader library containing the shaders. ElemShaderLibrary ShaderLibrary; - const char* AmplificationShaderFunction; // Function name of the mesh shader in the shader library. const char* MeshShaderFunction; // Function name of the pixel shader in the shader library. diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Common/DirectXShaderCompiler.cpp index dfe370c9..9d7c71c4 100644 --- a/src/ElementalTools/Common/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Common/DirectXShaderCompiler.cpp @@ -72,9 +72,6 @@ ReadOnlySpan GetShaderTypeTarget(DxilShaderKind shaderKind) case DxilShaderKind::Compute: return "cs_6_8"; - case DxilShaderKind::Amplification: - return "as_6_8"; - case DxilShaderKind::Mesh: return "ms_6_8"; @@ -93,9 +90,6 @@ ShaderType GetShaderTypeEnum(DxilShaderKind shaderKind) case DxilShaderKind::Compute: return ShaderType_Compute; - case DxilShaderKind::Amplification: - return ShaderType_Amplification; - case DxilShaderKind::Mesh: return ShaderType_Mesh; diff --git a/src/ElementalTools/Common/MetalShaderConverter.cpp b/src/ElementalTools/Common/MetalShaderConverter.cpp index 6a60f654..c03fad8d 100644 --- a/src/ElementalTools/Common/MetalShaderConverter.cpp +++ b/src/ElementalTools/Common/MetalShaderConverter.cpp @@ -53,9 +53,6 @@ IRShaderStage ConvertShaderTypeToMetalShaderStage(ShaderType shaderType) { switch (shaderType) { - case ShaderType_Amplification: - return IRShaderStage::IRShaderStageAmplification; - case ShaderType_Mesh: return IRShaderStage::IRShaderStageMesh; diff --git a/src/ElementalTools/Common/ShaderCompilerUtils.h b/src/ElementalTools/Common/ShaderCompilerUtils.h index 75ee1e8e..c66c9f5f 100644 --- a/src/ElementalTools/Common/ShaderCompilerUtils.h +++ b/src/ElementalTools/Common/ShaderCompilerUtils.h @@ -5,7 +5,6 @@ enum ShaderType { ShaderType_Unknown = 0, - ShaderType_Amplification = 1, ShaderType_Mesh = 2, ShaderType_Pixel = 3, ShaderType_Compute = 4, diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index a909e812..78525987 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -298,14 +298,13 @@ ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const return pipelineState; } -ElemPipelineState TestOpenMeshShaderAmplification(ElemGraphicsDevice graphicsDevice, const char* shader, const char* amplificationShaderFunction, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) +ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) { auto shaderLibrary = TestOpenShader(graphicsDevice, shader); ElemGraphicsPipelineStateParameters pipelineStateParameters = *baseParameters; pipelineStateParameters.ShaderLibrary = shaderLibrary; - pipelineStateParameters.AmplificationShaderFunction = amplificationShaderFunction; pipelineStateParameters.MeshShaderFunction = meshShaderFunction; pipelineStateParameters.PixelShaderFunction = pixelShaderFunction; @@ -320,11 +319,6 @@ ElemPipelineState TestOpenMeshShaderAmplification(ElemGraphicsDevice graphicsDev return pipelineState; } -ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters) -{ - return TestOpenMeshShaderAmplification(graphicsDevice, shader, nullptr, meshShaderFunction, pixelShaderFunction, baseParameters); -} - TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType) { ElemGraphicsHeapOptions heapOptions = diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index efa47f3f..4abe7de9 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -141,7 +141,6 @@ void TestInitLog(); ElemShaderLibrary TestOpenShader(ElemGraphicsDevice graphicsDevice, const char* shader); ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* function); -ElemPipelineState TestOpenMeshShaderAmplification(ElemGraphicsDevice graphicsDevice, const char* shader, const char* amplificationShaderFunction, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_Gpu); diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 9d1ddbab..a42519f4 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -161,129 +161,3 @@ UTEST(Rendering, DispatchMesh) ASSERT_LOG_NOERROR(); ASSERT_COLOR_BUFFER(bufferData, 1.0f, 1.0f, 0.0f, 1.0f); } - -UTEST(Rendering, DispatchMeshAmplificationShouldDraw) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - - ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; - ElemGraphicsPipelineStateParameters psoParameters = - { - .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } - }; - - auto meshShaderPipeline = TestOpenMeshShaderAmplification(graphicsDevice, "RenderingTests.shader", "AmplificationShader", "MeshShader", "PixelShader", &psoParameters); - - // Act - ElemRenderPassRenderTarget renderPassRenderTarget = - { - .RenderTarget = renderTarget.Texture, - .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, - .LoadAction = ElemRenderPassLoadAction_Clear - }; - - ElemBeginRenderPassParameters parameters = - { - .RenderTargets = - { - .Items = &renderPassRenderTarget, - .Length = 1 - } - }; - - ElemBeginRenderPass(commandList, ¶meters); - ElemBindPipelineState(commandList, meshShaderPipeline); - - uint32_t shouldDraw = 1u; - ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)&shouldDraw, .Length = sizeof(uint32_t) }); - - ElemDispatchMesh(commandList, 1, 1, 1); - ElemEndRenderPass(commandList); - - // Assert - ElemCommitCommandList(commandList); - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - ElemWaitForFenceOnCpu(fence); - - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); - uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); - - TestFreeGpuBuffer(readbackBuffer); - TestFreeGpuTexture(renderTarget); - ElemFreePipelineState(meshShaderPipeline); - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER(bufferData, 1.0f, 1.0f, 0.0f, 1.0f); -} - -UTEST(Rendering, DispatchMeshAmplificationShouldNotDraw) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - - ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; - ElemGraphicsPipelineStateParameters psoParameters = - { - .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } - }; - - auto meshShaderPipeline = TestOpenMeshShaderAmplification(graphicsDevice, "RenderingTests.shader", "AmplificationShader", "MeshShader", "PixelShader", &psoParameters); - - // Act - ElemRenderPassRenderTarget renderPassRenderTarget = - { - .RenderTarget = renderTarget.Texture, - .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, - .LoadAction = ElemRenderPassLoadAction_Clear - }; - - ElemBeginRenderPassParameters parameters = - { - .RenderTargets = - { - .Items = &renderPassRenderTarget, - .Length = 1 - } - }; - - ElemBeginRenderPass(commandList, ¶meters); - ElemBindPipelineState(commandList, meshShaderPipeline); - - uint32_t shouldDraw = 0u; - ElemPushPipelineStateConstants(commandList, 0, { .Items = (uint8_t*)&shouldDraw, .Length = sizeof(uint32_t) }); - - ElemDispatchMesh(commandList, 1, 1, 1); - ElemEndRenderPass(commandList); - - // Assert - ElemCommitCommandList(commandList); - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - ElemWaitForFenceOnCpu(fence); - - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); - uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); - - TestFreeGpuBuffer(readbackBuffer); - TestFreeGpuTexture(renderTarget); - ElemFreePipelineState(meshShaderPipeline); - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER(bufferData, 0.0f, 0.0f, 1.0f, 1.0f); -} From d420eb7db3d116b0f9308808c155894b8cc81868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 26 Aug 2024 20:23:24 +0200 Subject: [PATCH 18/93] cimgui external ref --- .gitmodules | 3 + external/CMakeLists.txt | 6 + external/cimgui | 1 + samples/CMakeLists.txt | 2 +- .../Elemental/05-HelloImGui/CMakeLists.txt | 9 ++ .../Data/RenderMesh.hlsl | 0 samples/Elemental/05-HelloImGui/main.c | 146 +++++++++++++++++ .../CMakeLists.txt | 0 .../06-HelloMesh/Data/RenderMesh.hlsl | 153 ++++++++++++++++++ .../{05-HelloMesh => 06-HelloMesh}/main.c | 0 .../Common/Graphics/Vulkan/VulkanShader.cpp | 15 -- .../Microsoft/Graphics/DirectX12Shader.cpp | 12 -- .../Microsoft/Graphics/DirectX12Shader.h | 1 - 13 files changed, 319 insertions(+), 29 deletions(-) create mode 160000 external/cimgui create mode 100644 samples/Elemental/05-HelloImGui/CMakeLists.txt rename samples/Elemental/{05-HelloMesh => 05-HelloImGui}/Data/RenderMesh.hlsl (100%) create mode 100644 samples/Elemental/05-HelloImGui/main.c rename samples/Elemental/{05-HelloMesh => 06-HelloMesh}/CMakeLists.txt (100%) create mode 100644 samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl rename samples/Elemental/{05-HelloMesh => 06-HelloMesh}/main.c (100%) diff --git a/.gitmodules b/.gitmodules index ef429247..403dd5da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "external/meshoptimizer"] path = external/meshoptimizer url = https://github.com/zeux/meshoptimizer.git +[submodule "external/cimgui"] + path = external/cimgui + url = https://github.com/cimgui/cimgui.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index d2b50a09..6f126dc3 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -222,6 +222,12 @@ target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compile add_library(xxHash INTERFACE) target_include_directories(xxHash INTERFACE ./xxHash/) +#======================================================================= +# ImGui +#======================================================================= +add_subdirectory(./cimgui) +set(IMGUI_STATIC "yes") + #======================================================================= # Volk #======================================================================= diff --git a/external/cimgui b/external/cimgui new file mode 160000 index 00000000..35a4e8f8 --- /dev/null +++ b/external/cimgui @@ -0,0 +1 @@ +Subproject commit 35a4e8f8932c6395156ffacee288b9c30e50cb63 diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 401868da..78b07363 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,3 +1,3 @@ add_library(SampleCommon INTERFACE) - +target_link_libraries(SampleCommon INTERFACE cimgui) target_include_directories(SampleCommon INTERFACE ./Common/) diff --git a/samples/Elemental/05-HelloImGui/CMakeLists.txt b/samples/Elemental/05-HelloImGui/CMakeLists.txt new file mode 100644 index 00000000..ea99acb5 --- /dev/null +++ b/samples/Elemental/05-HelloImGui/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SAMPLE_NAME HelloImGui) + +add_executable(${SAMPLE_NAME} main.c Data) +target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) +target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) +add_dependencies(${SAMPLE_NAME} SampleSharedData) + +configure_resource_compilation(${SAMPLE_NAME} resource_list) +configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl similarity index 100% rename from samples/Elemental/05-HelloMesh/Data/RenderMesh.hlsl rename to samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c new file mode 100644 index 00000000..613188da --- /dev/null +++ b/samples/Elemental/05-HelloImGui/main.c @@ -0,0 +1,146 @@ +#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS +#include +#include "Elemental.h" +#include "SampleUtils.h" + +typedef struct +{ + bool PreferVulkan; + ElemWindow Window; + ElemGraphicsDevice GraphicsDevice; + ElemCommandQueue CommandQueue; + ElemGraphicsHeap GraphicsHeap; + uint32_t CurrentHeapOffset; + ElemFence LastExecutionFence; + ElemSwapChain SwapChain; + ElemGraphicsHeap DepthBufferHeap; + ElemGraphicsResource DepthBuffer; + ElemPipelineState GraphicsPipeline; + } ApplicationPayload; + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); + +void InitSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + applicationPayload->Window = ElemCreateWindow(NULL); + + ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->PreferVulkan }); + applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); + + applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); + applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); + ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); + + // TODO: For now we create a separate heap to avoid memory management + applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); + + // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues + applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); + + applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { + .DebugName = "RenderMesh PSO", + .ShaderLibrary = shaderLibrary, + .MeshShaderFunction = "MeshMain", + .PixelShaderFunction = "PixelMain", + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, + .DepthStencil = + { + .Format = ElemGraphicsFormat_D32_FLOAT, + .DepthCompareFunction = ElemGraphicsCompareFunction_Greater + } + }); + + ElemFreeShaderLibrary(shaderLibrary); + SampleStartFrameMeasurement(); +} + +void FreeSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); + + ElemFreePipelineState(applicationPayload->GraphicsPipeline); + ElemFreeSwapChain(applicationPayload->SwapChain); + ElemFreeCommandQueue(applicationPayload->CommandQueue); + + ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); + ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); + + ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); + ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); +} + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + + ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) { + .RenderTargets = + { + .Items = (ElemRenderPassRenderTarget[]) { + { + .RenderTarget = updateParameters->BackBufferRenderTarget, + .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, + }}, + .Length = 1 + }, + .DepthStencil = + { + .DepthStencil = applicationPayload->DepthBuffer + } + }); + + bool open = true; +// igShowDemoWindow(&open); + + ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); + //ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); + //ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + + ElemEndRenderPass(commandList); + + ElemCommitCommandList(commandList); + applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); + + ElemPresentSwapChain(applicationPayload->SwapChain); + SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); + + if (frameMeasurement.HasNewData) + { + SampleSetWindowTitle(applicationPayload->Window, "HelloImGui", applicationPayload->GraphicsDevice, frameMeasurement.FrameTimeInSeconds, frameMeasurement.Fps); + } + + SampleStartFrameMeasurement(); +} + +int main(int argc, const char* argv[]) +{ + bool preferVulkan = false; + + if (argc > 1 && strcmp(argv[1], "--vulkan") == 0) + { + preferVulkan = true; + } + + ElemConfigureLogHandler(ElemConsoleLogHandler); + + ApplicationPayload payload = + { + .PreferVulkan = preferVulkan + }; + + ElemRunApplication(&(ElemRunApplicationParameters) + { + .ApplicationName = "Hello ImGui", + .InitHandler = InitSample, + .FreeHandler = FreeSample, + .Payload = &payload + }); +} diff --git a/samples/Elemental/05-HelloMesh/CMakeLists.txt b/samples/Elemental/06-HelloMesh/CMakeLists.txt similarity index 100% rename from samples/Elemental/05-HelloMesh/CMakeLists.txt rename to samples/Elemental/06-HelloMesh/CMakeLists.txt diff --git a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl new file mode 100644 index 00000000..819a0583 --- /dev/null +++ b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl @@ -0,0 +1,153 @@ +struct ShaderParameters +{ + uint32_t VertexBufferIndex; + uint32_t MeshletBufferIndex; + uint32_t MeshletVertexIndexBufferIndex; + uint32_t MeshletTriangleIndexBufferIndex; + float4 RotationQuaternion; + float Zoom; + float AspectRatio; + uint32_t TriangleColor; + uint32_t MeshletCount; +}; + +[[vk::push_constant]] +ShaderParameters parameters : register(b0); + +struct ElemMeshlet +{ + uint32_t VertexIndexOffset; + uint32_t VertexIndexCount; + uint32_t TriangleOffset; + uint32_t TriangleCount; +}; + +struct Vertex +{ + float3 Position; + float3 Normal; + float2 TextureCoordinates; +}; + +struct VertexOutput +{ + float4 Position: SV_Position; + float3 WorldNormal: NORMAL0; + uint MeshletIndex : COLOR0; +}; + +// TODO: Put that in an include file +float4x4 TransformMatrix(float4 quaternion, float3 translation) +{ + float xw = quaternion.x*quaternion.w, xx = quaternion.x*quaternion.x, yy = quaternion.y*quaternion.y, + yw = quaternion.y*quaternion.w, xy = quaternion.x*quaternion.y, yz = quaternion.y*quaternion.z, + zw = quaternion.z*quaternion.w, xz = quaternion.x*quaternion.z, zz = quaternion.z*quaternion.z; + + float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); + float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); + float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); + float4 row4 = float4(0.0, 0.0, 0.0, 1.0); + + return float4x4(row1, row2, row3, row4); +} + +float4x4 LookAtLHMatrix(float3 eyePosition, float3 targetPosition, float3 upDirection) +{ + float3 forwardDirection = normalize(targetPosition - eyePosition); + float3 rightDirection = normalize(cross(upDirection, forwardDirection)); + float3 upDirectionNew = cross(forwardDirection, rightDirection); + + float4 row1 = float4(rightDirection.x, upDirectionNew.x, forwardDirection.x, 0.0); + float4 row2 = float4(rightDirection.y, upDirectionNew.y, forwardDirection.y, 0.0); + float4 row3 = float4(rightDirection.z, upDirectionNew.z, forwardDirection.z, 0.0); + float4 row4 = float4(-dot(rightDirection, eyePosition), -dot(upDirectionNew, eyePosition), -dot(forwardDirection, eyePosition), 1.0); + + return float4x4(row1, row2, row3, row4); +} + +float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) +{ + float height = 1.0 / tan(fovY * 0.5); + + float4 row1 = float4(height / aspectRatio, 0.0f, 0.0f, 0.0f); + float4 row2 = float4(0.0f, height, 0.0f, 0.0f); + float4 row3 = float4(0.0f, 0.0f, 0, 1.0f); + float4 row4 = float4(0.0f, 0.0f, zNear, 0.0f); + + return float4x4(row1, row2, row3, row4); +} + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(126, 1, 1)] +void MeshMain(in uint groupId: SV_GroupID, + in uint groupThreadId : SV_GroupThreadID, + out vertices VertexOutput vertices[64], + out indices uint3 indices[126]) +{ + uint meshletIndex = groupId; + + ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; + ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); + + SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); + + if (groupThreadId < meshlet.VertexIndexCount) + { + // TODO: Compute matrix in task shader + float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; + + float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); + float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); + float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); + + float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); + float4x4 inverseTransposeWorldMatrix = worldMatrix; + + ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; + ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; + + uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); + Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); + + vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); + vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].MeshletIndex = groupId; + } + + if (groupThreadId < meshlet.TriangleCount) + { + ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; + uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); + + indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; + } +} + +uint hash(uint a) +{ + a = (a+0x7ed55d16) + (a<<12); + a = (a^0xc761c23c) ^ (a>>19); + a = (a+0x165667b1) + (a<<5); + a = (a+0xd3a2646c) ^ (a<<9); + a = (a+0xfd7046c5) + (a<<3); + a = (a^0xb55a4f09) ^ (a>>16); + + return a; +} + +[shader("pixel")] +float4 PixelMain(const VertexOutput input) : SV_Target0 +{ + if (parameters.TriangleColor == 0) + { + return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); + } + else + { + uint hashResult = hash(input.MeshletIndex); + float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; + + return float4(meshletColor, 1.0); + } +} diff --git a/samples/Elemental/05-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c similarity index 100% rename from samples/Elemental/05-HelloMesh/main.c rename to samples/Elemental/06-HelloMesh/main.c diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 0b169eab..965d25b1 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -67,9 +67,6 @@ VkShaderStageFlagBits ConvertShaderTypeToVulkan(ShaderType shaderType) { switch (shaderType) { - case ShaderType_Amplification: - return VK_SHADER_STAGE_TASK_BIT_EXT; - case ShaderType_Mesh: return VK_SHADER_STAGE_MESH_BIT_EXT; @@ -355,18 +352,6 @@ ElemPipelineState VulkanCompileGraphicsPipelineState(ElemGraphicsDevice graphics VkPipelineShaderStageCreateInfo stages[3] = {}; uint32_t stageCount = 0; - if (parameters->AmplificationShaderFunction) - { - auto shaderStage = GetVulkanShaderFunctionStageCreateInfo(stackMemoryArena, shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); - - if (shaderStage.stage == VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM) - { - return ELEM_HANDLE_NULL; - } - - stages[stageCount++] = shaderStage; - } - if (parameters->MeshShaderFunction) { auto shaderStage = GetVulkanShaderFunctionStageCreateInfo(stackMemoryArena, shaderLibraryData, ShaderType_Mesh, parameters->MeshShaderFunction); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index a0850976..c7cc092b 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -330,18 +330,6 @@ ComPtr CreateDirectX12OldPSO(ElemGraphicsDevice graphicsDev GraphicsPso psoDesc = {}; psoDesc.RootSignature = graphicsDeviceData->RootSignature.Get(); - if (parameters->AmplificationShaderFunction) - { - auto shaderByteCode = GetDirectX12ShaderFunctionByteCode(shaderLibraryData, ShaderType_Amplification, parameters->AmplificationShaderFunction); - - if (!shaderByteCode.pShaderBytecode) - { - return ELEM_HANDLE_NULL; - } - - psoDesc.AS = shaderByteCode; - } - if (parameters->MeshShaderFunction) { auto shaderByteCode = GetDirectX12ShaderFunctionByteCode(shaderLibraryData, ShaderType_Mesh, parameters->MeshShaderFunction); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.h b/src/Elemental/Microsoft/Graphics/DirectX12Shader.h index 46ce1dca..b88357d3 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.h @@ -59,7 +59,6 @@ struct GraphicsPso { public: PsoSubObject(RootSignature, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE, ID3D12RootSignature*); - PsoSubObject(AS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_AS, D3D12_SHADER_BYTECODE); PsoSubObject(MS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MS, D3D12_SHADER_BYTECODE); PsoSubObject(PS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS, D3D12_SHADER_BYTECODE); PsoSubObject(RenderTargets, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS, D3D12_RT_FORMAT_ARRAY); From c8678e2f1551bace8a4b7c197a05e293b4ebbffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 27 Aug 2024 06:35:41 +0200 Subject: [PATCH 19/93] ImGui Update --- external/CMakeLists.txt | 2 +- samples/Elemental/05-HelloImGui/main.c | 41 ++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6f126dc3..6f8ec215 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -226,7 +226,7 @@ target_include_directories(xxHash INTERFACE ./xxHash/) # ImGui #======================================================================= add_subdirectory(./cimgui) -set(IMGUI_STATIC "yes") +#set(IMGUI_STATIC "yes") #======================================================================= # Volk diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 613188da..e49e0987 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -6,6 +6,7 @@ typedef struct { bool PreferVulkan; + bool ShowImGuiDemoWindow; ElemWindow Window; ElemGraphicsDevice GraphicsDevice; ElemCommandQueue CommandQueue; @@ -17,9 +18,38 @@ typedef struct ElemGraphicsResource DepthBuffer; ElemPipelineState GraphicsPipeline; } ApplicationPayload; + +typedef struct +{ + ElemGraphicsDevice GraphicsDevice; +} ImGuiElementalUserData; void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); +void ImGuiInitBackend(ElemGraphicsDevice graphicsDevice) +{ + ImGuiIO* imGuiIO = igGetIO(); + + ImGuiElementalUserData* backendData = malloc(sizeof(ImGuiElementalUserData)); + memset(backendData, 0, sizeof(ImGuiElementalUserData)); + imGuiIO->BackendRendererUserData = backendData; + imGuiIO->BackendRendererName = "Elemental"; + + // TODO: + imGuiIO->Fonts->TexID = (void*)1; +} + +void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters) +{ + ImGuiIO* imGuiIO = igGetIO(); + + imGuiIO->DisplaySize = (ImVec2){ updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height }; +} + +void ImGuiRenderDrawData(ImDrawData* draw_data, ElemCommandList commandList) +{ +} + void InitSample(void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; @@ -56,6 +86,9 @@ void InitSample(void* payload) ElemFreeShaderLibrary(shaderLibrary); SampleStartFrameMeasurement(); + + igCreateContext(NULL); + ImGuiInitBackend(applicationPayload->GraphicsDevice); } void FreeSample(void* payload) @@ -97,8 +130,12 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void } }); - bool open = true; -// igShowDemoWindow(&open); + ImGuidElementalNewFrame(updateParameters); + igNewFrame(); + igShowDemoWindow(&applicationPayload->ShowImGuiDemoWindow); + igRender(); + + ImGuiRenderDrawData(igGetDrawData(), commandList); ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); //ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); From e88dd671a90f8d22d0956f527021026eca36fcb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 27 Aug 2024 16:23:35 +0200 Subject: [PATCH 20/93] ImGui WIP --- samples/CMakeLists.txt | 1 - .../Elemental/05-HelloImGui/CMakeLists.txt | 1 + .../05-HelloImGui/Data/RenderImGui.hlsl | 62 +++++++ .../05-HelloImGui/Data/RenderMesh.hlsl | 153 ---------------- samples/Elemental/05-HelloImGui/main.c | 168 ++++++++++++------ .../Common/DirectXShaderCompiler.cpp | 1 + 6 files changed, 179 insertions(+), 207 deletions(-) create mode 100644 samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl delete mode 100644 samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 78b07363..4b860f38 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,3 +1,2 @@ add_library(SampleCommon INTERFACE) -target_link_libraries(SampleCommon INTERFACE cimgui) target_include_directories(SampleCommon INTERFACE ./Common/) diff --git a/samples/Elemental/05-HelloImGui/CMakeLists.txt b/samples/Elemental/05-HelloImGui/CMakeLists.txt index ea99acb5..d83797ab 100644 --- a/samples/Elemental/05-HelloImGui/CMakeLists.txt +++ b/samples/Elemental/05-HelloImGui/CMakeLists.txt @@ -2,6 +2,7 @@ set(SAMPLE_NAME HelloImGui) add_executable(${SAMPLE_NAME} main.c Data) target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) +target_link_libraries(${SAMPLE_NAME} PUBLIC cimgui) target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) add_dependencies(${SAMPLE_NAME} SampleSharedData) diff --git a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl new file mode 100644 index 00000000..7b4ccbb3 --- /dev/null +++ b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl @@ -0,0 +1,62 @@ +struct ShaderParameters +{ + uint VertexBufferIndex; + uint IndexBufferIndex; + uint VertexOffset; + uint VertexCount; + uint IndexOffset; + uint IndexCount; + uint RenderWidth; + uint RenderHeight; +}; + +[[vk::push_constant]] +ShaderParameters parameters : register(b0); + +struct ImDrawVert +{ + float2 Position; + float2 TextureCoordinates; + uint Color; +}; + +struct VertexOutput +{ + float4 Position: SV_Position; +}; + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(128, 1, 1)] +void MeshMain(in uint groupId: SV_GroupID, + in uint groupThreadId : SV_GroupThreadID, + out vertices VertexOutput vertices[64], + out indices uint3 indices[128]) +{ + // TODO: Compute correct count + uint vertexCount = 4;//parameters.VertexCount / 64; + uint triangleCount = 1;//parameters.IndexCount / 3 / 64; + SetMeshOutputCounts(parameters.VertexCount, triangleCount); + + if (groupThreadId < vertexCount) + { + ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[parameters.IndexBufferIndex]; + ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; + + uint vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupThreadId) * sizeof(uint16_t)); + ImDrawVert vertex = vertexBuffer.Load(parameters.VertexOffset + vertexIndex * sizeof(ImDrawVert)); + + vertices[groupThreadId].Position = float4(vertex.Position.x / parameters.RenderWidth, vertex.Position.y / parameters.RenderHeight, 0.0, 1.0); + } + + if (groupThreadId < triangleCount) + { + indices[groupThreadId] = uint3(groupThreadId * 3, groupThreadId * 3 + 1, groupThreadId * 3 + 2); + } +} + +[shader("pixel")] +float4 PixelMain(const VertexOutput input) : SV_Target0 +{ + return float4(1.0, 1.0, 0.0, 1.0); +} diff --git a/samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl deleted file mode 100644 index 819a0583..00000000 --- a/samples/Elemental/05-HelloImGui/Data/RenderMesh.hlsl +++ /dev/null @@ -1,153 +0,0 @@ -struct ShaderParameters -{ - uint32_t VertexBufferIndex; - uint32_t MeshletBufferIndex; - uint32_t MeshletVertexIndexBufferIndex; - uint32_t MeshletTriangleIndexBufferIndex; - float4 RotationQuaternion; - float Zoom; - float AspectRatio; - uint32_t TriangleColor; - uint32_t MeshletCount; -}; - -[[vk::push_constant]] -ShaderParameters parameters : register(b0); - -struct ElemMeshlet -{ - uint32_t VertexIndexOffset; - uint32_t VertexIndexCount; - uint32_t TriangleOffset; - uint32_t TriangleCount; -}; - -struct Vertex -{ - float3 Position; - float3 Normal; - float2 TextureCoordinates; -}; - -struct VertexOutput -{ - float4 Position: SV_Position; - float3 WorldNormal: NORMAL0; - uint MeshletIndex : COLOR0; -}; - -// TODO: Put that in an include file -float4x4 TransformMatrix(float4 quaternion, float3 translation) -{ - float xw = quaternion.x*quaternion.w, xx = quaternion.x*quaternion.x, yy = quaternion.y*quaternion.y, - yw = quaternion.y*quaternion.w, xy = quaternion.x*quaternion.y, yz = quaternion.y*quaternion.z, - zw = quaternion.z*quaternion.w, xz = quaternion.x*quaternion.z, zz = quaternion.z*quaternion.z; - - float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); - float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); - float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); - float4 row4 = float4(0.0, 0.0, 0.0, 1.0); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 LookAtLHMatrix(float3 eyePosition, float3 targetPosition, float3 upDirection) -{ - float3 forwardDirection = normalize(targetPosition - eyePosition); - float3 rightDirection = normalize(cross(upDirection, forwardDirection)); - float3 upDirectionNew = cross(forwardDirection, rightDirection); - - float4 row1 = float4(rightDirection.x, upDirectionNew.x, forwardDirection.x, 0.0); - float4 row2 = float4(rightDirection.y, upDirectionNew.y, forwardDirection.y, 0.0); - float4 row3 = float4(rightDirection.z, upDirectionNew.z, forwardDirection.z, 0.0); - float4 row4 = float4(-dot(rightDirection, eyePosition), -dot(upDirectionNew, eyePosition), -dot(forwardDirection, eyePosition), 1.0); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) -{ - float height = 1.0 / tan(fovY * 0.5); - - float4 row1 = float4(height / aspectRatio, 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, height, 0.0f, 0.0f); - float4 row3 = float4(0.0f, 0.0f, 0, 1.0f); - float4 row4 = float4(0.0f, 0.0f, zNear, 0.0f); - - return float4x4(row1, row2, row3, row4); -} - -[shader("mesh")] -[OutputTopology("triangle")] -[NumThreads(126, 1, 1)] -void MeshMain(in uint groupId: SV_GroupID, - in uint groupThreadId : SV_GroupThreadID, - out vertices VertexOutput vertices[64], - out indices uint3 indices[126]) -{ - uint meshletIndex = groupId; - - ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; - ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); - - SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); - - if (groupThreadId < meshlet.VertexIndexCount) - { - // TODO: Compute matrix in task shader - float cameraZDistance = (parameters.AspectRatio >= 0.75 ? -2.0 : -4.0) + parameters.Zoom; - - float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); - float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); - float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); - - float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); - float4x4 inverseTransposeWorldMatrix = worldMatrix; - - ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - - uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); - Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); - - vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); - vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose - vertices[groupThreadId].MeshletIndex = groupId; - } - - if (groupThreadId < meshlet.TriangleCount) - { - ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; - uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); - - indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; - } -} - -uint hash(uint a) -{ - a = (a+0x7ed55d16) + (a<<12); - a = (a^0xc761c23c) ^ (a>>19); - a = (a+0x165667b1) + (a<<5); - a = (a+0xd3a2646c) ^ (a<<9); - a = (a+0xfd7046c5) + (a<<3); - a = (a^0xb55a4f09) ^ (a>>16); - - return a; -} - -[shader("pixel")] -float4 PixelMain(const VertexOutput input) : SV_Target0 -{ - if (parameters.TriangleColor == 0) - { - return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); - } - else - { - uint hashResult = hash(input.MeshletIndex); - float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; - - return float4(meshletColor, 1.0); - } -} diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index e49e0987..16c27e7f 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -3,6 +3,28 @@ #include "Elemental.h" #include "SampleUtils.h" +typedef struct +{ + uint32_t FontTextureId; + ElemGraphicsResource VertexBuffer; + ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; + ElemGraphicsResource IndexBuffer; + ElemGraphicsResourceDescriptor IndexBufferReadDescriptor; + ElemPipelineState RenderPipeline; +} ElemImGuiBackendData; + +typedef struct +{ + uint32_t VertexBufferIndex; + uint32_t IndexBufferIndex; + uint32_t VertexOffset; + uint32_t VertexCount; + uint32_t IndexOffset; + uint32_t IndexCount; + uint32_t RenderWidth; + uint32_t RenderHeight; +} ImGuiShaderParameters; + typedef struct { bool PreferVulkan; @@ -14,29 +36,58 @@ typedef struct uint32_t CurrentHeapOffset; ElemFence LastExecutionFence; ElemSwapChain SwapChain; - ElemGraphicsHeap DepthBufferHeap; - ElemGraphicsResource DepthBuffer; - ElemPipelineState GraphicsPipeline; - } ApplicationPayload; - -typedef struct -{ - ElemGraphicsDevice GraphicsDevice; -} ImGuiElementalUserData; + ElemImGuiBackendData ImGuiBackendData; +} ApplicationPayload; void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); -void ImGuiInitBackend(ElemGraphicsDevice graphicsDevice) +void CreateBuffer(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, uint32_t sizeInBytes) +{ + // TODO: Alignment should be used with the offset before adding the size of the resource! + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); + + applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); + *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); + applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; + + *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); +} + +void ImGuiInitBackend(ApplicationPayload* payload) { + ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; + ImGuiIO* imGuiIO = igGetIO(); - - ImGuiElementalUserData* backendData = malloc(sizeof(ImGuiElementalUserData)); - memset(backendData, 0, sizeof(ImGuiElementalUserData)); - imGuiIO->BackendRendererUserData = backendData; + imGuiIO->BackendRendererUserData = &payload->ImGuiBackendData; imGuiIO->BackendRendererName = "Elemental"; - // TODO: - imGuiIO->Fonts->TexID = (void*)1; + CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawVert)); + CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawIdx)); + + // TODO: Font + uint8_t* fontPixels; + uint32_t fontWidth; + uint32_t fontHeight; + uint32_t fontBytesPerPixel; + ImFontAtlas_GetTexDataAsRGBA32(imGuiIO->Fonts, &fontPixels, &fontWidth, &fontHeight, &fontBytesPerPixel); + + payload->ImGuiBackendData.FontTextureId = 1; + imGuiIO->Fonts->TexID = (void*)&imGuiData->FontTextureId; + + ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(payload->SwapChain); + + ElemDataSpan shaderData = SampleReadFile(!payload->PreferVulkan ? "RenderImGui.shader": "RenderImGui_vulkan.shader"); + ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(payload->GraphicsDevice, shaderData); + + imGuiData->RenderPipeline = ElemCompileGraphicsPipelineState(payload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { + .DebugName = "RenderImGui PSO", + .ShaderLibrary = shaderLibrary, + .MeshShaderFunction = "MeshMain", + .PixelShaderFunction = "PixelMain", + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, + }); + + ElemFreeShaderLibrary(shaderLibrary); } void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters) @@ -46,8 +97,47 @@ void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParamete imGuiIO->DisplaySize = (ImVec2){ updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height }; } -void ImGuiRenderDrawData(ImDrawData* draw_data, ElemCommandList commandList) +void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, ApplicationPayload* payload, const ElemSwapChainUpdateParameters* updateParameters) { + ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; + + printf("DrawData VertexCount=%d, IndexCount=%d, CommandList=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount , drawData->CmdListsCount); + + uint32_t currentVertexOffset = 0; + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(imGuiData->VertexBuffer); + + uint32_t currentIndexOffset = 0; + ElemDataSpan indexBufferPointer = ElemGetGraphicsResourceDataSpan(imGuiData->IndexBuffer); + + for (int32_t i = 0; i < drawData->CmdListsCount; i++) + { + ImDrawList* imCommandList = drawData->CmdLists.Data[i]; + + ImGuiShaderParameters shaderParameters = + { + .VertexBufferIndex = imGuiData->VertexBufferReadDescriptor, + .IndexBufferIndex = imGuiData->IndexBufferReadDescriptor, + .VertexOffset = currentVertexOffset, + .VertexCount = imCommandList->VtxBuffer.Size, + .IndexOffset = currentIndexOffset, + .IndexCount = imCommandList->IdxBuffer.Size, + .RenderWidth = updateParameters->SwapChainInfo.Width, + .RenderHeight = updateParameters->SwapChainInfo.Height + }; + + memcpy(vertexBufferPointer.Items + currentVertexOffset, imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); + currentVertexOffset += imCommandList->VtxBuffer.Size; + + memcpy(indexBufferPointer.Items + currentIndexOffset, imCommandList->IdxBuffer.Data, imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx)); + currentIndexOffset += imCommandList->IdxBuffer.Size; + + ElemBindPipelineState(commandList, imGuiData->RenderPipeline); + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&shaderParameters, .Length = sizeof(ImGuiShaderParameters) }); + + uint32_t threadSize = 128; + //ElemDispatchMesh(commandList, (imCommandList->IdxBuffer.Size / 3 + (threadSize - 1)) / threadSize, 1, 1); + ElemDispatchMesh(commandList, 1, 1, 1); + } } void InitSample(void* payload) @@ -60,35 +150,13 @@ void InitSample(void* payload) applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); - ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); - // TODO: For now we create a separate heap to avoid memory management - applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); - - // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); - ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); - - applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { - .DebugName = "RenderMesh PSO", - .ShaderLibrary = shaderLibrary, - .MeshShaderFunction = "MeshMain", - .PixelShaderFunction = "PixelMain", - .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, - .DepthStencil = - { - .Format = ElemGraphicsFormat_D32_FLOAT, - .DepthCompareFunction = ElemGraphicsCompareFunction_Greater - } - }); - - ElemFreeShaderLibrary(shaderLibrary); SampleStartFrameMeasurement(); igCreateContext(NULL); - ImGuiInitBackend(applicationPayload->GraphicsDevice); + ImGuiInitBackend(applicationPayload); } void FreeSample(void* payload) @@ -97,13 +165,15 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); - ElemFreePipelineState(applicationPayload->GraphicsPipeline); + ElemFreePipelineState(applicationPayload->ImGuiBackendData.RenderPipeline); + ElemFreeGraphicsResource(applicationPayload->ImGuiBackendData.VertexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(applicationPayload->ImGuiBackendData.VertexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->ImGuiBackendData.IndexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(applicationPayload->ImGuiBackendData.IndexBufferReadDescriptor, NULL); + ElemFreeSwapChain(applicationPayload->SwapChain); ElemFreeCommandQueue(applicationPayload->CommandQueue); - ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); - ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); - ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); } @@ -123,10 +193,6 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, }}, .Length = 1 - }, - .DepthStencil = - { - .DepthStencil = applicationPayload->DepthBuffer } }); @@ -135,11 +201,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void igShowDemoWindow(&applicationPayload->ShowImGuiDemoWindow); igRender(); - ImGuiRenderDrawData(igGetDrawData(), commandList); - - ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); - //ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - //ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + ImGuiRenderDrawData(igGetDrawData(), commandList, applicationPayload, updateParameters); ElemEndRenderPass(commandList); diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Common/DirectXShaderCompiler.cpp index 9d7c71c4..d7ee2745 100644 --- a/src/ElementalTools/Common/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Common/DirectXShaderCompiler.cpp @@ -131,6 +131,7 @@ ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn parameters[parameterIndex++] = L"-HV"; parameters[parameterIndex++] = L"202x"; + parameters[parameterIndex++] = L"-enable-16bit-types"; parameters[parameterIndex++] = DXC_ARG_ALL_RESOURCES_BOUND; From 14dbc4b2c099bb013e8d6d7e1fc0667ccf8748e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 28 Aug 2024 06:38:59 +0200 Subject: [PATCH 21/93] ImGui WIP --- .gitignore | 1 + .../05-HelloImGui/Data/RenderImGui.hlsl | 42 ++++++++++++++----- samples/Elemental/05-HelloImGui/main.c | 30 ++++++++++--- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 580866c9..7257fc06 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.resolved *.obj *.mesh +*.ini xcuserdata/ packages/ diff --git a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl index 7b4ccbb3..7a698db6 100644 --- a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl +++ b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl @@ -23,30 +23,52 @@ struct ImDrawVert struct VertexOutput { float4 Position: SV_Position; + float2 TextureCoordinates: TEXCOORD0; + float4 Color: COLOR0; }; +float4x4 CreateOrthographicMatrixOffCenter(float minPlaneX, float maxPlaneX, float minPlaneY, float maxPlaneY) +{ + float4 row1 = float4(2.0f / (maxPlaneX - minPlaneX), 0.0f, 0.0f, 0.0f); + float4 row2 = float4(0.0f, 2.0f / (maxPlaneY - minPlaneY), 0.0f, 0.0f); + float4 row3 = float4(0.0f, 0.0f, 0.5, 1.0f); + float4 row4 = float4((minPlaneX + maxPlaneX) / (minPlaneX - maxPlaneX), (minPlaneY + maxPlaneY) / (minPlaneY - maxPlaneY), 0.5, 1.0f); + + return float4x4(row1, row2, row3, row4); +} + [shader("mesh")] [OutputTopology("triangle")] -[NumThreads(128, 1, 1)] +[NumThreads(126, 1, 1)] void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThreadID, - out vertices VertexOutput vertices[64], - out indices uint3 indices[128]) + out vertices VertexOutput vertices[126], + out indices uint3 indices[64]) { // TODO: Compute correct count - uint vertexCount = 4;//parameters.VertexCount / 64; - uint triangleCount = 1;//parameters.IndexCount / 3 / 64; - SetMeshOutputCounts(parameters.VertexCount, triangleCount); + uint vertexCount = 126;//parameters.VertexCount / 64; + uint triangleCount = 42;//parameters.IndexCount / 3 / 64; + SetMeshOutputCounts(vertexCount, triangleCount); if (groupThreadId < vertexCount) { + float4x4 projectionMatrix = CreateOrthographicMatrixOffCenter(0, parameters.RenderWidth, parameters.RenderHeight, 0); + ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[parameters.IndexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - uint vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupThreadId) * sizeof(uint16_t)); - ImDrawVert vertex = vertexBuffer.Load(parameters.VertexOffset + vertexIndex * sizeof(ImDrawVert)); + uint16_t vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupId * 126 + groupThreadId) * sizeof(uint16_t)); + ImDrawVert vertex = vertexBuffer.Load((parameters.VertexOffset + vertexIndex) * sizeof(ImDrawVert)); + + float4 output = mul(float4(vertex.Position.x, vertex.Position.y, 0.0, 1.0), projectionMatrix); + float4 color = unpack_u8u32(vertex.Color) / 255.0; + color.rgb *= color.a; + color = float4(pow(abs(color.rgb), 2.2f), 1.0 - pow(abs(1.0 - color.a), 2.2f)); + //color = pow(abs(color), 2.2f); - vertices[groupThreadId].Position = float4(vertex.Position.x / parameters.RenderWidth, vertex.Position.y / parameters.RenderHeight, 0.0, 1.0); + vertices[groupThreadId].Position = output; + vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; + vertices[groupThreadId].Color = color; } if (groupThreadId < triangleCount) @@ -58,5 +80,5 @@ void MeshMain(in uint groupId: SV_GroupID, [shader("pixel")] float4 PixelMain(const VertexOutput input) : SV_Target0 { - return float4(1.0, 1.0, 0.0, 1.0); + return input.Color; } diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 16c27e7f..b9b95ea9 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -60,6 +60,8 @@ void ImGuiInitBackend(ApplicationPayload* payload) ImGuiIO* imGuiIO = igGetIO(); imGuiIO->BackendRendererUserData = &payload->ImGuiBackendData; imGuiIO->BackendRendererName = "Elemental"; + imGuiIO->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; + //imGuiIO->ConfigFlags |= ImGuiConfigFlags_IsSRGB; CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawVert)); CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawIdx)); @@ -84,7 +86,20 @@ void ImGuiInitBackend(ApplicationPayload* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, + .RenderTargets = + { + .Items = (ElemGraphicsPipelineStateRenderTarget[]) { + { + .Format = swapChainInfo.Format, + //.BlendOperation = ElemGraphicsBlendOperation_Add, + //.SourceBlendFactor = ElemGraphicsBlendFactor_One, + //.DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, + //.BlendOperationAlpha = ElemGraphicsBlendOperation_Add, + //.SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One, + //.DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_InverseSourceAlpha + }}, + .Length = 1 + }, }); ElemFreeShaderLibrary(shaderLibrary); @@ -94,7 +109,12 @@ void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParamete { ImGuiIO* imGuiIO = igGetIO(); - imGuiIO->DisplaySize = (ImVec2){ updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height }; + // TODO: Set Correct UI Scale + imGuiIO->DisplaySize = (ImVec2){ (float)updateParameters->SwapChainInfo.Width / 2.0f, (float)updateParameters->SwapChainInfo.Height / 2.0f }; + imGuiIO->DisplayFramebufferScale = (ImVec2) { 2.0f, 2.0f }; + + //imGuiIO->DisplaySize = (ImVec2){ updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height }; + //imGuiIO->ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts | ImGuiConfigFlags_DpiEnableScaleViewports; } void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, ApplicationPayload* payload, const ElemSwapChainUpdateParameters* updateParameters) @@ -121,8 +141,8 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl .VertexCount = imCommandList->VtxBuffer.Size, .IndexOffset = currentIndexOffset, .IndexCount = imCommandList->IdxBuffer.Size, - .RenderWidth = updateParameters->SwapChainInfo.Width, - .RenderHeight = updateParameters->SwapChainInfo.Height + .RenderWidth = drawData->DisplaySize.x, + .RenderHeight = drawData->DisplaySize.y }; memcpy(vertexBufferPointer.Items + currentVertexOffset, imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); @@ -136,7 +156,7 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl uint32_t threadSize = 128; //ElemDispatchMesh(commandList, (imCommandList->IdxBuffer.Size / 3 + (threadSize - 1)) / threadSize, 1, 1); - ElemDispatchMesh(commandList, 1, 1, 1); + ElemDispatchMesh(commandList, 10, 1, 1); } } From c7398e2beccb8d12f2bfb5dc3dbdfecec1028660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 28 Aug 2024 16:16:50 +0200 Subject: [PATCH 22/93] ImGui WIP --- .../05-HelloImGui/Data/RenderImGui.hlsl | 30 +++--- samples/Elemental/05-HelloImGui/main.c | 99 ++++++++++++------- samples/Elemental/06-HelloMesh/main.c | 1 + .../Apple/Graphics/MetalSwapChain.cpp | 8 +- src/Elemental/Apple/Graphics/MetalSwapChain.h | 1 + .../Graphics/Vulkan/VulkanSwapChain.cpp | 8 +- .../Common/Graphics/Vulkan/VulkanSwapChain.h | 1 + src/Elemental/Elemental.h | 2 + .../Microsoft/Graphics/DirectX12SwapChain.cpp | 8 +- .../Microsoft/Graphics/DirectX12SwapChain.h | 1 + 10 files changed, 103 insertions(+), 56 deletions(-) diff --git a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl index 7a698db6..3f22a7b3 100644 --- a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl +++ b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl @@ -3,7 +3,6 @@ struct ShaderParameters uint VertexBufferIndex; uint IndexBufferIndex; uint VertexOffset; - uint VertexCount; uint IndexOffset; uint IndexCount; uint RenderWidth; @@ -30,43 +29,42 @@ struct VertexOutput float4x4 CreateOrthographicMatrixOffCenter(float minPlaneX, float maxPlaneX, float minPlaneY, float maxPlaneY) { float4 row1 = float4(2.0f / (maxPlaneX - minPlaneX), 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, 2.0f / (maxPlaneY - minPlaneY), 0.0f, 0.0f); + float4 row2 = float4(0.0f, 2.0f / (minPlaneY - maxPlaneY), 0.0f, 0.0f); float4 row3 = float4(0.0f, 0.0f, 0.5, 1.0f); - float4 row4 = float4((minPlaneX + maxPlaneX) / (minPlaneX - maxPlaneX), (minPlaneY + maxPlaneY) / (minPlaneY - maxPlaneY), 0.5, 1.0f); + float4 row4 = float4((minPlaneX + maxPlaneX) / (minPlaneX - maxPlaneX), (minPlaneY + maxPlaneY) / (maxPlaneY - minPlaneY), 0.5, 1.0f); return float4x4(row1, row2, row3, row4); } +#define MAX_VERTEX_COUNT 126 + [shader("mesh")] [OutputTopology("triangle")] -[NumThreads(126, 1, 1)] +[NumThreads(MAX_VERTEX_COUNT, 1, 1)] void MeshMain(in uint groupId: SV_GroupID, in uint groupThreadId : SV_GroupThreadID, - out vertices VertexOutput vertices[126], - out indices uint3 indices[64]) + out vertices VertexOutput vertices[MAX_VERTEX_COUNT], + out indices uint3 indices[MAX_VERTEX_COUNT / 3]) { - // TODO: Compute correct count - uint vertexCount = 126;//parameters.VertexCount / 64; - uint triangleCount = 42;//parameters.IndexCount / 3 / 64; + // TODO: For the moment we process several time the same vertices + uint vertexCount = min(MAX_VERTEX_COUNT, parameters.IndexCount - groupId * MAX_VERTEX_COUNT); + uint triangleCount = vertexCount / 3; SetMeshOutputCounts(vertexCount, triangleCount); if (groupThreadId < vertexCount) { - float4x4 projectionMatrix = CreateOrthographicMatrixOffCenter(0, parameters.RenderWidth, parameters.RenderHeight, 0); + float4x4 projectionMatrix = CreateOrthographicMatrixOffCenter(0, parameters.RenderWidth, 0, parameters.RenderHeight); ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[parameters.IndexBufferIndex]; ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - uint16_t vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupId * 126 + groupThreadId) * sizeof(uint16_t)); + uint16_t vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupId * MAX_VERTEX_COUNT + groupThreadId) * sizeof(uint16_t)); ImDrawVert vertex = vertexBuffer.Load((parameters.VertexOffset + vertexIndex) * sizeof(ImDrawVert)); - float4 output = mul(float4(vertex.Position.x, vertex.Position.y, 0.0, 1.0), projectionMatrix); float4 color = unpack_u8u32(vertex.Color) / 255.0; - color.rgb *= color.a; - color = float4(pow(abs(color.rgb), 2.2f), 1.0 - pow(abs(1.0 - color.a), 2.2f)); - //color = pow(abs(color), 2.2f); + color = float4(pow(color.rgb, 2.2f), color.a); - vertices[groupThreadId].Position = output; + vertices[groupThreadId].Position = mul(float4(vertex.Position.x, vertex.Position.y, 0.0, 1.0), projectionMatrix); vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; vertices[groupThreadId].Color = color; } diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index b9b95ea9..3d8741e7 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -18,7 +18,6 @@ typedef struct uint32_t VertexBufferIndex; uint32_t IndexBufferIndex; uint32_t VertexOffset; - uint32_t VertexCount; uint32_t IndexOffset; uint32_t IndexCount; uint32_t RenderWidth; @@ -63,8 +62,8 @@ void ImGuiInitBackend(ApplicationPayload* payload) imGuiIO->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; //imGuiIO->ConfigFlags |= ImGuiConfigFlags_IsSRGB; - CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawVert)); - CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 5000 * sizeof(ImDrawIdx)); + CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 10000 * sizeof(ImDrawVert)); + CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 10000 * sizeof(ImDrawIdx)); // TODO: Font uint8_t* fontPixels; @@ -86,17 +85,18 @@ void ImGuiInitBackend(ApplicationPayload* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", + .CullMode = ElemGraphicsCullMode_None, .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) { { .Format = swapChainInfo.Format, - //.BlendOperation = ElemGraphicsBlendOperation_Add, - //.SourceBlendFactor = ElemGraphicsBlendFactor_One, - //.DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, - //.BlendOperationAlpha = ElemGraphicsBlendOperation_Add, - //.SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One, - //.DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_InverseSourceAlpha + .BlendOperation = ElemGraphicsBlendOperation_Add, + .SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlpha, + .DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, + .BlendOperationAlpha = ElemGraphicsBlendOperation_Add, + .SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One, + .DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_InverseSourceAlpha }}, .Length = 1 }, @@ -105,15 +105,38 @@ void ImGuiInitBackend(ApplicationPayload* payload) ElemFreeShaderLibrary(shaderLibrary); } -void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters) +void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters, const ElemInputStream inputStream) { ImGuiIO* imGuiIO = igGetIO(); - // TODO: Set Correct UI Scale - imGuiIO->DisplaySize = (ImVec2){ (float)updateParameters->SwapChainInfo.Width / 2.0f, (float)updateParameters->SwapChainInfo.Height / 2.0f }; - imGuiIO->DisplayFramebufferScale = (ImVec2) { 2.0f, 2.0f }; + imGuiIO->DisplaySize = (ImVec2) + { + (float)updateParameters->SwapChainInfo.Width / updateParameters->SwapChainInfo.UIScale, + (float)updateParameters->SwapChainInfo.Height / updateParameters->SwapChainInfo.UIScale + }; + + imGuiIO->DisplayFramebufferScale = (ImVec2) { updateParameters->SwapChainInfo.UIScale, updateParameters->SwapChainInfo.UIScale }; + imGuiIO->DeltaTime = updateParameters->DeltaTimeInSeconds; + + for (uint32_t i = 0; i < inputStream.Events.Length; i++) + { + ElemInputEvent* inputEvent = &inputStream.Events.Items[i]; + + if (inputEvent->InputId == ElemInputId_MouseAxisXNegative || + inputEvent->InputId == ElemInputId_MouseAxisXPositive || + inputEvent->InputId == ElemInputId_MouseAxisYNegative || + inputEvent->InputId == ElemInputId_MouseAxisYPositive) + { + ElemWindowCursorPosition cursorPosition = ElemGetWindowCursorPosition(updateParameters->SwapChainInfo.Window); + ImGuiIO_AddMousePosEvent(imGuiIO, cursorPosition.X / updateParameters->SwapChainInfo.UIScale, cursorPosition.Y / updateParameters->SwapChainInfo.UIScale); + } + + if (inputEvent->InputId == ElemInputId_MouseLeftButton) + { + ImGuiIO_AddMouseButtonEvent(imGuiIO, ImGuiMouseButton_Left, inputEvent->Value); + } + } - //imGuiIO->DisplaySize = (ImVec2){ updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height }; //imGuiIO->ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts | ImGuiConfigFlags_DpiEnableScaleViewports; } @@ -121,8 +144,6 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl { ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; - printf("DrawData VertexCount=%d, IndexCount=%d, CommandList=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount , drawData->CmdListsCount); - uint32_t currentVertexOffset = 0; ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(imGuiData->VertexBuffer); @@ -131,32 +152,40 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl for (int32_t i = 0; i < drawData->CmdListsCount; i++) { + printf("DrawData: Vertexcount = %d, IndexCount=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount); ImDrawList* imCommandList = drawData->CmdLists.Data[i]; - ImGuiShaderParameters shaderParameters = + for (int32_t j = 0; j < imCommandList->CmdBuffer.Size; j++) { - .VertexBufferIndex = imGuiData->VertexBufferReadDescriptor, - .IndexBufferIndex = imGuiData->IndexBufferReadDescriptor, - .VertexOffset = currentVertexOffset, - .VertexCount = imCommandList->VtxBuffer.Size, - .IndexOffset = currentIndexOffset, - .IndexCount = imCommandList->IdxBuffer.Size, - .RenderWidth = drawData->DisplaySize.x, - .RenderHeight = drawData->DisplaySize.y - }; + ImDrawCmd* drawCommand = &imCommandList->CmdBuffer.Data[j]; + + ImGuiShaderParameters shaderParameters = + { + .VertexBufferIndex = imGuiData->VertexBufferReadDescriptor, + .IndexBufferIndex = imGuiData->IndexBufferReadDescriptor, + .VertexOffset = currentVertexOffset + drawCommand->VtxOffset, + .IndexOffset = currentIndexOffset + drawCommand->IdxOffset, + .IndexCount = drawCommand->ElemCount, + .RenderWidth = drawData->DisplaySize.x, + .RenderHeight = drawData->DisplaySize.y + }; + + // TODO: SetScisorTests + // TODO: Bug when clicking on the menu + + ElemBindPipelineState(commandList, imGuiData->RenderPipeline); + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&shaderParameters, .Length = sizeof(ImGuiShaderParameters) }); + + uint32_t threadSize = 126; + uint32_t dispatchCount = (drawCommand->ElemCount + (threadSize - 1)) / threadSize; + ElemDispatchMesh(commandList, dispatchCount, 1, 1); + } memcpy(vertexBufferPointer.Items + currentVertexOffset, imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); currentVertexOffset += imCommandList->VtxBuffer.Size; memcpy(indexBufferPointer.Items + currentIndexOffset, imCommandList->IdxBuffer.Data, imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx)); currentIndexOffset += imCommandList->IdxBuffer.Size; - - ElemBindPipelineState(commandList, imGuiData->RenderPipeline); - ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&shaderParameters, .Length = sizeof(ImGuiShaderParameters) }); - - uint32_t threadSize = 128; - //ElemDispatchMesh(commandList, (imCommandList->IdxBuffer.Size / 3 + (threadSize - 1)) / threadSize, 1, 1); - ElemDispatchMesh(commandList, 10, 1, 1); } } @@ -201,6 +230,8 @@ void FreeSample(void* payload) void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + ElemInputStream inputStream = ElemGetInputStream(); ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); @@ -216,7 +247,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void } }); - ImGuidElementalNewFrame(updateParameters); + ImGuidElementalNewFrame(updateParameters, inputStream); igNewFrame(); igShowDemoWindow(&applicationPayload->ShowImGuiDemoWindow); igRender(); diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index a681e2cb..959c8b80 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -315,6 +315,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void } SampleModelViewerInputActions* inputActions = &applicationPayload->InputActions; + // TODO: PAss the input stream as a parameter SampleUpdateInputActions(&applicationPayload->InputActionBindings); if (inputActions->ExitApp) diff --git a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp index 18218ab0..f344363f 100644 --- a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp +++ b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp @@ -49,7 +49,7 @@ MetalSwapChainDataFull* GetMetalSwapChainDataFull(ElemSwapChain swapChain) return SystemGetDataPoolItemFull(metalSwapChainPool, swapChain); } -void ResizeMetalSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height) +void ResizeMetalSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height, float uiScale) { if (width == 0 || height == 0) { @@ -64,6 +64,7 @@ void ResizeMetalSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t heig swapChainData->Width = width; swapChainData->Height = height; swapChainData->AspectRatio = (float)width / height; + swapChainData->UIScale = uiScale; swapChainData->DeviceObject->setDrawableSize(CGSizeMake(width, height)); } @@ -151,6 +152,7 @@ ElemSwapChain MetalCreateSwapChain(ElemCommandQueue commandQueue, ElemWindow win .Width = width, .Height = height, .AspectRatio = (float)width / height, + .UIScale = windowRenderSize.UIScale, .Format = ElemGraphicsFormat_B8G8R8A8_SRGB // TODO: Temporary }); @@ -185,9 +187,11 @@ ElemSwapChainInfo MetalGetSwapChainInfo(ElemSwapChain swapChain) return { + .Window = swapChainData->Window, .Width = swapChainData->Width, .Height = swapChainData->Height, .AspectRatio = swapChainData->AspectRatio, + .UIScale = swapChainData->UIScale, .Format = swapChainData->Format }; } @@ -266,7 +270,7 @@ void MetalDisplayLinkHandler::metalDisplayLinkNeedsUpdate(CA::MetalDisplayLink* if (windowSize.Width > 0 && windowSize.Height > 0 && (windowSize.Width != swapChainData->Width || windowSize.Height != swapChainData->Height)) { - ResizeMetalSwapChain(_swapChain, windowSize.Width, windowSize.Height); + ResizeMetalSwapChain(_swapChain, windowSize.Width, windowSize.Height, windowSize.UIScale); sizeChanged = true; } diff --git a/src/Elemental/Apple/Graphics/MetalSwapChain.h b/src/Elemental/Apple/Graphics/MetalSwapChain.h index 5282b5ae..9fa83d70 100644 --- a/src/Elemental/Apple/Graphics/MetalSwapChain.h +++ b/src/Elemental/Apple/Graphics/MetalSwapChain.h @@ -28,6 +28,7 @@ struct MetalSwapChainData uint32_t Width; uint32_t Height; float AspectRatio; + float UIScale; ElemGraphicsFormat Format; }; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 0f9c92fc..ba96aa88 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -113,7 +113,7 @@ VkSwapchainKHR CreateVulkanSwapChainObject(ElemGraphicsDevice graphicsDevice, Vk return swapChain; } -void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height) +void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height, float uiScale) { if (width == 0 || height == 0) { @@ -138,6 +138,7 @@ void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t hei swapChainData->Width = width; swapChainData->Height = height; swapChainData->AspectRatio = (float)width / height; + swapChainData->UIScale = uiScale; auto fence = CreateVulkanCommandQueueFence(swapChainData->CommandQueue); VulkanWaitForFenceOnCpu(fence); @@ -208,7 +209,7 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) if (windowSize.Width > 0 && windowSize.Height > 0 && (windowSize.Width != swapChainData->Width || windowSize.Height != swapChainData->Height)) { - ResizeVulkanSwapChain(handle, windowSize.Width, windowSize.Height); + ResizeVulkanSwapChain(handle, windowSize.Width, windowSize.Height, windowSize.UIScale); sizeChanged = true; } @@ -409,6 +410,7 @@ ElemSwapChain VulkanCreateSwapChain(ElemCommandQueue commandQueue, ElemWindow wi .Width = width, .Height = height, .AspectRatio = (float)width / height, + .UIScale = windowRenderSize.UIScale, .Format = ElemGraphicsFormat_B8G8R8A8_SRGB, // TODO: change that .FrameLatency = frameLatency, .TargetFPS = targetFPS @@ -475,9 +477,11 @@ ElemSwapChainInfo VulkanGetSwapChainInfo(ElemSwapChain swapChain) return { + .Window = swapChainData->Window, .Width = swapChainData->Width, .Height = swapChainData->Height, .AspectRatio = swapChainData->AspectRatio, + .UIScale = swapChainData->UIScale, .Format = swapChainData->Format }; } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h index c2e89047..d7cf81e2 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h @@ -28,6 +28,7 @@ struct VulkanSwapChainData uint32_t Width; uint32_t Height; float AspectRatio; + float UIScale; ElemGraphicsFormat Format; bool PresentCalled; uint64_t PresentId; diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index fbeceadf..6e8e32a1 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -634,12 +634,14 @@ typedef struct */ typedef struct { + ElemWindow Window; // Width of the swap chain in pixels. uint32_t Width; // Height of the swap chain in pixels. uint32_t Height; // Aspect ratio of the swap chain. float AspectRatio; + float UIScale; // Format of the textures used in the swap chain. ElemGraphicsFormat Format; } ElemSwapChainInfo; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index 1abf17c7..9a39eda0 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -56,7 +56,7 @@ void CreateDirectX12SwapChainRenderTargetViews(ElemSwapChain swapChain) } } -void ResizeDirectX12SwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height) +void ResizeDirectX12SwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t height, float uiScale) { if (width == 0 || height == 0) { @@ -72,6 +72,7 @@ void ResizeDirectX12SwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t swapChainData->Width = width; swapChainData->Height = height; swapChainData->AspectRatio = (float)width / height; + swapChainData->UIScale = uiScale; auto fence = CreateDirectX12CommandQueueFence(swapChainData->CommandQueue); ElemWaitForFenceOnCpu(fence); @@ -207,7 +208,7 @@ void CheckDirectX12AvailableSwapChain(ElemHandle handle) if (windowSize.Width > 0 && windowSize.Height > 0 && (windowSize.Width != swapChainData->Width || windowSize.Height != swapChainData->Height)) { - ResizeDirectX12SwapChain(handle, windowSize.Width, windowSize.Height); + ResizeDirectX12SwapChain(handle, windowSize.Width, windowSize.Height, windowSize.UIScale); sizeChanged = true; } @@ -360,6 +361,7 @@ ElemSwapChain DirectX12CreateSwapChain(ElemCommandQueue commandQueue, ElemWindow .Width = width, .Height = height, .AspectRatio = (float)width / height, + .UIScale = windowRenderSize.UIScale, .Format = ElemGraphicsFormat_B8G8R8A8_SRGB, // TODO: change that .FrameLatency = frameLatency, .TargetFPS = targetFPS @@ -410,9 +412,11 @@ ElemSwapChainInfo DirectX12GetSwapChainInfo(ElemSwapChain swapChain) return { + .Window = swapChainData->Window, .Width = swapChainData->Width, .Height = swapChainData->Height, .AspectRatio = swapChainData->AspectRatio, + .UIScale = swapChainData->UIScale, .Format = swapChainData->Format }; } diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h index 20668391..5c494287 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h @@ -19,6 +19,7 @@ struct DirectX12SwapChainData uint32_t Width; uint32_t Height; float AspectRatio; + float UIScale; ElemGraphicsFormat Format; bool PresentCalled; uint32_t FrameLatency; From ea0e82a4b11b812bc7b12c43a797b80d4825e84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 29 Aug 2024 16:58:31 +0200 Subject: [PATCH 23/93] Implement Viewport and Scissor tests --- samples/Common/SampleInputs.h | 4 +- samples/Elemental/04-HelloCompute/main.c | 3 +- samples/Elemental/05-HelloImGui/main.c | 26 +- samples/Elemental/06-HelloMesh/main.c | 3 +- src/Elemental/Common/Graphics/Rendering.cpp | 16 + .../Graphics/Vulkan/VulkanRendering.cpp | 28 +- src/Elemental/Elemental.h | 29 ++ src/Elemental/ElementalLoader.c | 38 +++ .../Microsoft/Graphics/DirectX12Rendering.cpp | 46 ++- .../Microsoft/Graphics/DirectX12SwapChain.cpp | 6 + tests/GraphicsTests/GraphicsTests.h | 38 +++ tests/GraphicsTests/RenderingTests.cpp | 295 +++++++++++++++++- 12 files changed, 503 insertions(+), 29 deletions(-) diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index 43831e3c..41b77c6b 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -97,10 +97,8 @@ void SampleRegisterInputActionBinding(SampleInputActionBindingSpan* bindings, El }; } -void SampleUpdateInputActions(SampleInputActionBindingSpan* inputActionBindings) +void SampleUpdateInputActions(SampleInputActionBindingSpan* inputActionBindings, ElemInputStream inputStream) { - ElemInputStream inputStream = ElemGetInputStream(); - for (uint32_t i = 0; i < inputActionBindings->Length; i++) { SampleInputActionBinding binding = inputActionBindings->Items[i]; diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index c931de05..31acbf98 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -257,8 +257,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void applicationPayload->ShaderParameters.RenderTextureIndex = applicationPayload->RenderTextureWriteDescriptor; + ElemInputStream inputStream = ElemGetInputStream(); SampleStandardInputActions* inputActions = &applicationPayload->InputActions; - SampleUpdateInputActions(&applicationPayload->InputActionBindings); + SampleUpdateInputActions(&applicationPayload->InputActionBindings, inputStream); if (inputActions->ExitApp) { diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 3d8741e7..f2dcb24d 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -62,14 +62,15 @@ void ImGuiInitBackend(ApplicationPayload* payload) imGuiIO->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; //imGuiIO->ConfigFlags |= ImGuiConfigFlags_IsSRGB; - CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 10000 * sizeof(ImDrawVert)); - CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 10000 * sizeof(ImDrawIdx)); + // TODO: Dynamically manage heaps and buffers + CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 20000 * sizeof(ImDrawVert)); + CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 20000 * sizeof(ImDrawIdx)); // TODO: Font uint8_t* fontPixels; - uint32_t fontWidth; - uint32_t fontHeight; - uint32_t fontBytesPerPixel; + int32_t fontWidth; + int32_t fontHeight; + int32_t fontBytesPerPixel; ImFontAtlas_GetTexDataAsRGBA32(imGuiIO->Fonts, &fontPixels, &fontWidth, &fontHeight, &fontBytesPerPixel); payload->ImGuiBackendData.FontTextureId = 1; @@ -152,7 +153,7 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl for (int32_t i = 0; i < drawData->CmdListsCount; i++) { - printf("DrawData: Vertexcount = %d, IndexCount=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount); + printf("DrawData: Vertexcount = %d, IndexCount=%d, CommandLists=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount, drawData->CmdLists.Size); ImDrawList* imCommandList = drawData->CmdLists.Data[i]; for (int32_t j = 0; j < imCommandList->CmdBuffer.Size; j++) @@ -170,21 +171,24 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl .RenderHeight = drawData->DisplaySize.y }; - // TODO: SetScisorTests - // TODO: Bug when clicking on the menu - ElemBindPipelineState(commandList, imGuiData->RenderPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&shaderParameters, .Length = sizeof(ImGuiShaderParameters) }); + ElemSetScissorRectangle(commandList, &(ElemRectangle) { + .X = drawCommand->ClipRect.x * updateParameters->SwapChainInfo.UIScale, + .Y = drawCommand->ClipRect.y * updateParameters->SwapChainInfo.UIScale, + .Width = (drawCommand->ClipRect.z - drawCommand->ClipRect.x) * updateParameters->SwapChainInfo.UIScale, + .Height = (drawCommand->ClipRect.w - drawCommand->ClipRect.y) * updateParameters->SwapChainInfo.UIScale + }); uint32_t threadSize = 126; uint32_t dispatchCount = (drawCommand->ElemCount + (threadSize - 1)) / threadSize; ElemDispatchMesh(commandList, dispatchCount, 1, 1); } - memcpy(vertexBufferPointer.Items + currentVertexOffset, imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(vertexBufferPointer.Items + currentVertexOffset * sizeof(ImDrawVert), imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); currentVertexOffset += imCommandList->VtxBuffer.Size; - memcpy(indexBufferPointer.Items + currentIndexOffset, imCommandList->IdxBuffer.Data, imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx)); + memcpy(indexBufferPointer.Items + currentIndexOffset * sizeof(ImDrawIdx), imCommandList->IdxBuffer.Data, imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx)); currentIndexOffset += imCommandList->IdxBuffer.Size; } } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index 959c8b80..9d0b2477 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -315,8 +315,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void } SampleModelViewerInputActions* inputActions = &applicationPayload->InputActions; + ElemInputStream inputStream = ElemGetInputStream(); // TODO: PAss the input stream as a parameter - SampleUpdateInputActions(&applicationPayload->InputActionBindings); + SampleUpdateInputActions(&applicationPayload->InputActionBindings, inputStream); if (inputActions->ExitApp) { diff --git a/src/Elemental/Common/Graphics/Rendering.cpp b/src/Elemental/Common/Graphics/Rendering.cpp index d4cce4ec..97374f07 100644 --- a/src/Elemental/Common/Graphics/Rendering.cpp +++ b/src/Elemental/Common/Graphics/Rendering.cpp @@ -27,6 +27,22 @@ ElemAPI void ElemSetViewports(ElemCommandList commandList, ElemViewportSpan view DispatchGraphicsFunction(SetViewports, commandList, viewports); } +ElemAPI void ElemSetScissorRectangle(ElemCommandList commandList, const ElemRectangle* rectangle) +{ + ElemRectangleSpan rectangleSpan = + { + .Items = (ElemRectangle*)rectangle, + .Length = 1 + }; + + DispatchGraphicsFunction(SetScissorRectangles, commandList, rectangleSpan); +} + +ElemAPI void ElemSetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles) +{ + DispatchGraphicsFunction(SetScissorRectangles, commandList, rectangles); +} + ElemAPI void ElemDispatchMesh(ElemCommandList commandList, uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ) { DispatchGraphicsFunction(DispatchMesh, commandList, threadGroupCountX, threadGroupCountY, threadGroupCountZ); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp index 3b690c19..f5380331 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp @@ -274,7 +274,6 @@ void VulkanSetViewports(ElemCommandList commandList, ElemViewportSpan viewports) auto stackMemoryArena = SystemGetStackMemoryArena(); auto vulkanViewports = SystemPushArray(stackMemoryArena, viewports.Length); - auto vulkanScissorRects = SystemPushArray(stackMemoryArena, viewports.Length); for (uint32_t i = 0; i < viewports.Length; i++) { @@ -287,18 +286,36 @@ void VulkanSetViewports(ElemCommandList commandList, ElemViewportSpan viewports) .minDepth = viewports.Items[i].MinDepth, .maxDepth = viewports.Items[i].MaxDepth }; + } + + auto commandListData = GetVulkanCommandListData(commandList); + SystemAssert(commandListData); + + vkCmdSetViewport(commandListData->DeviceObject, 0, vulkanViewports.Length, vulkanViewports.Pointer); +} + +void VulkanSetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles) +{ + // TODO: Check command list type != COMPUTE + + SystemAssert(commandList != ELEM_HANDLE_NULL); + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto vulkanScissorRects = SystemPushArray(stackMemoryArena, rectangles.Length); + + for (uint32_t i = 0; i < rectangles.Length; i++) + { vulkanScissorRects[i] = { .offset = { - .x = (int32_t)viewports.Items[i].X, - .y = (int32_t)viewports.Items[i].Y, + .x = (int32_t)rectangles.Items[i].X, + .y = (int32_t)rectangles.Items[i].Y, }, .extent = { - .width = (uint32_t)viewports.Items[i].Width, - .height = (uint32_t)viewports.Items[i].Height + .width = (uint32_t)rectangles.Items[i].Width, + .height = (uint32_t)rectangles.Items[i].Height } }; } @@ -306,7 +323,6 @@ void VulkanSetViewports(ElemCommandList commandList, ElemViewportSpan viewports) auto commandListData = GetVulkanCommandListData(commandList); SystemAssert(commandListData); - vkCmdSetViewport(commandListData->DeviceObject, 0, vulkanViewports.Length, vulkanViewports.Pointer); vkCmdSetScissor(commandListData->DeviceObject, 0, vulkanScissorRects.Length, vulkanScissorRects.Pointer); } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 6e8e32a1..38eaa736 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -804,6 +804,29 @@ typedef struct float Alpha; } ElemColor; +typedef struct +{ + // X coordinate of the rectangle's top left corner. + float X; + // Y coordinate of the rectangle's top left corner. + float Y; + // Width of the rectangle. + float Width; + // Height of the rectangle. + float Height; +} ElemRectangle; + +/** + * Represents a collection of rectangles. + */ +typedef struct +{ + // Pointer to an array of ElemRectangle. + ElemRectangle* Items; + // Number of items in the array. + uint32_t Length; +} ElemRectangleSpan; + /** * Defines a viewport for rendering. */ @@ -878,6 +901,8 @@ typedef struct ElemRenderPassDepthBufferStencil DepthStencil; // Viewports to be used in the render pass. ElemViewportSpan Viewports; + + ElemRectangleSpan ScissorRectangles; } ElemBeginRenderPassParameters; /** @@ -1116,6 +1141,10 @@ ElemAPI void ElemSetViewport(ElemCommandList commandList, const ElemViewport* vi */ ElemAPI void ElemSetViewports(ElemCommandList commandList, ElemViewportSpan viewports); +ElemAPI void ElemSetScissorRectangle(ElemCommandList commandList, const ElemRectangle* rectangle); + +ElemAPI void ElemSetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles); + /** * Dispatches a mesh shader operation on a command list, specifying the number of thread groups in each dimension. * @param commandList The command list on which the mesh operation is to be dispatched. diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index 6a9acf63..395e75e6 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -79,6 +79,8 @@ typedef struct ElementalFunctions void (*ElemEndRenderPass)(ElemCommandList); void (*ElemSetViewport)(ElemCommandList, ElemViewport const *); void (*ElemSetViewports)(ElemCommandList, ElemViewportSpan); + void (*ElemSetScissorRectangle)(ElemCommandList, ElemRectangle const *); + void (*ElemSetScissorRectangles)(ElemCommandList, ElemRectangleSpan); void (*ElemDispatchMesh)(ElemCommandList, unsigned int, unsigned int, unsigned int); ElemInputDeviceInfo (*ElemGetInputDeviceInfo)(ElemInputDevice); ElemInputStream (*ElemGetInputStream)(void); @@ -190,6 +192,8 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemEndRenderPass = (void (*)(ElemCommandList))GetElementalFunctionPointer("ElemEndRenderPass"); listElementalFunctions.ElemSetViewport = (void (*)(ElemCommandList, ElemViewport const *))GetElementalFunctionPointer("ElemSetViewport"); listElementalFunctions.ElemSetViewports = (void (*)(ElemCommandList, ElemViewportSpan))GetElementalFunctionPointer("ElemSetViewports"); + listElementalFunctions.ElemSetScissorRectangle = (void (*)(ElemCommandList, ElemRectangle const *))GetElementalFunctionPointer("ElemSetScissorRectangle"); + listElementalFunctions.ElemSetScissorRectangles = (void (*)(ElemCommandList, ElemRectangleSpan))GetElementalFunctionPointer("ElemSetScissorRectangles"); listElementalFunctions.ElemDispatchMesh = (void (*)(ElemCommandList, unsigned int, unsigned int, unsigned int))GetElementalFunctionPointer("ElemDispatchMesh"); listElementalFunctions.ElemGetInputDeviceInfo = (ElemInputDeviceInfo (*)(ElemInputDevice))GetElementalFunctionPointer("ElemGetInputDeviceInfo"); listElementalFunctions.ElemGetInputStream = (ElemInputStream (*)(void))GetElementalFunctionPointer("ElemGetInputStream"); @@ -1602,6 +1606,40 @@ static inline void ElemSetViewports(ElemCommandList commandList, ElemViewportSpa listElementalFunctions.ElemSetViewports(commandList, viewports); } +static inline void ElemSetScissorRectangle(ElemCommandList commandList, ElemRectangle const * rectangle) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + return; + } + + if (!listElementalFunctions.ElemSetScissorRectangle) + { + assert(listElementalFunctions.ElemSetScissorRectangle); + return; + } + + listElementalFunctions.ElemSetScissorRectangle(commandList, rectangle); +} + +static inline void ElemSetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + return; + } + + if (!listElementalFunctions.ElemSetScissorRectangles) + { + assert(listElementalFunctions.ElemSetScissorRectangles); + return; + } + + listElementalFunctions.ElemSetScissorRectangles(commandList, rectangles); +} + static inline void ElemDispatchMesh(ElemCommandList commandList, unsigned int threadGroupCountX, unsigned int threadGroupCountY, unsigned int threadGroupCountZ) { if (!LoadElementalFunctionPointers()) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp index c999d41a..33af446d 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Rendering.cpp @@ -125,6 +125,19 @@ void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRender ElemSetViewport(commandList, &viewport); } + + if (i == 0 && parameters->ScissorRectangles.Length == 0) + { + ElemRectangle rectangle = + { + .X = 0, + .Y = 0, + .Width = (float)textureData->Width, + .Height = (float)textureData->Height + }; + + ElemSetScissorRectangle(commandList, &rectangle); + } } D3D12_RENDER_PASS_DEPTH_STENCIL_DESC depthStencilDesc = {}; @@ -173,6 +186,11 @@ void DirectX12BeginRenderPass(ElemCommandList commandList, const ElemBeginRender ElemSetViewports(commandList, parameters->Viewports); } + if (parameters->ScissorRectangles.Length > 0) + { + ElemSetScissorRectangles(commandList, parameters->ScissorRectangles); + } + InsertDirectX12ResourceBarriersIfNeeded(commandList, ElemGraphicsResourceBarrierSyncType_RenderTarget); commandListData->DeviceObject->BeginRenderPass(renderTargetDescList.Length, renderTargetDescList.Pointer, parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL ? &depthStencilDesc : nullptr, D3D12_RENDER_PASS_FLAG_NONE); } @@ -225,7 +243,6 @@ void DirectX12SetViewports(ElemCommandList commandList, ElemViewportSpan viewpor auto stackMemoryArena = SystemGetStackMemoryArena(); auto directX12Viewports = SystemPushArray(stackMemoryArena, viewports.Length); - auto directX12ScissorRects = SystemPushArray(stackMemoryArena, viewports.Length); for (uint32_t i = 0; i < viewports.Length; i++) { @@ -238,20 +255,37 @@ void DirectX12SetViewports(ElemCommandList commandList, ElemViewportSpan viewpor .MinDepth = viewports.Items[i].MinDepth, .MaxDepth = viewports.Items[i].MaxDepth }; + } + + auto commandListData = GetDirectX12CommandListData(commandList); + SystemAssert(commandListData); + + commandListData->DeviceObject->RSSetViewports(directX12Viewports.Length, directX12Viewports.Pointer); +} + +void DirectX12SetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles) +{ + // TODO: Check command list type != COMPUTE + + SystemAssert(commandList != ELEM_HANDLE_NULL); + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto directX12ScissorRects = SystemPushArray(stackMemoryArena, rectangles.Length); + + for (uint32_t i = 0; i < rectangles.Length; i++) + { directX12ScissorRects[i] = { - .left = (int32_t)viewports.Items[i].X, - .top = (int32_t)viewports.Items[i].Y, - .right = (int32_t)viewports.Items[i].X + (int32_t)viewports.Items[i].Width, - .bottom = (int32_t)viewports.Items[i].Y + (int32_t)viewports.Items[i].Height + .left = (int32_t)rectangles.Items[i].X, + .top = (int32_t)rectangles.Items[i].Y, + .right = (int32_t)rectangles.Items[i].X + (int32_t)rectangles.Items[i].Width, + .bottom = (int32_t)rectangles.Items[i].Y + (int32_t)rectangles.Items[i].Height }; } auto commandListData = GetDirectX12CommandListData(commandList); SystemAssert(commandListData); - commandListData->DeviceObject->RSSetViewports(directX12Viewports.Length, directX12Viewports.Pointer); commandListData->DeviceObject->RSSetScissorRects(directX12ScissorRects.Length, directX12ScissorRects.Pointer); } diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index 9a39eda0..f0ee6151 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -194,6 +194,12 @@ void CheckDirectX12AvailableSwapChain(ElemHandle handle) // TODO: If delta time is above a thresold, take the delta time based on target FPS auto deltaTime = vSyncDelta * refreshInterval;//(nextVSyncQPCTime.QuadPart - swapChainData->PreviousTargetPresentationTimestamp.QuadPart) / ticksPerSecond; + + if (deltaTime <= 0.0f) + { + deltaTime = 1.0f / 60.0f; + } + swapChainData->PreviousTargetPresentationTimestamp = nextVSyncQPCTime; if (vSyncDelta > 1) diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 4abe7de9..9ab731b5 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -10,6 +10,18 @@ #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) #endif +#define PRINT_COLOR_BUFFER(buffer, totalWidth) \ + { \ + auto floatData = (float*)buffer.Items; \ + \ + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) \ + { \ + auto pixelX = (i / 4) % totalWidth;\ + auto pixelY = (i / 4) / totalWidth;\ + printf("Pixel %d [%d, %d]: %f %f %f %f\n", i / 4, pixelX, pixelY, floatData[i], floatData[i + 1], floatData[i + 2], floatData[i + 3]); \ + } \ + } + #define ASSERT_LOG_NOERROR() { TestInitLog(); ASSERT_FALSE_MSG(testHasLogErrors, testErrorLogs); } #define ASSERT_LOG_MESSAGE(message) { TestInitLog(); ASSERT_TRUE_MSG(strstr(testErrorLogs, message) != NULL, message); } #define ASSERT_LOG_MESSAGE_DEBUG(message) { TestInitLog(); ASSERT_TRUE_MSG(strstr(testDebugLogs, message) != NULL, message); } @@ -27,6 +39,32 @@ } \ } +#define ASSERT_COLOR_BUFFER_RECTANGLE(buffer, totalWidth, x, y, width, height, red, green, blue, alpha, backgroundRed, backgroundGreen, backgroundBlue, backgroundAlpha) \ + { \ + auto floatData = (float*)buffer.Items;\ + \ + for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) \ + { \ + auto pixelX = (i / 4) % totalWidth;\ + auto pixelY = (i / 4) / totalWidth;\ + \ + if (pixelX >= x && pixelX < (x + width) && pixelY >= y && pixelY < (y + height)) \ + { \ + ASSERT_EQ_MSG(floatData[i], red, "Red channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 1], green, "Green channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 2], blue, "Blue channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 3], alpha, "Alpha channel data is invalid."); \ + } \ + else \ + { \ + ASSERT_EQ_MSG(floatData[i], backgroundRed, "Background Red channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 1], backgroundGreen, "Background Green channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 2], backgroundBlue, "Background Blue channel data is invalid."); \ + ASSERT_EQ_MSG(floatData[i + 3], backgroundAlpha, "Background Alpha channel data is invalid."); \ + } \ + } \ + } + #define BUFFER_BARRIER(resource, syncBefore, syncAfter, accessBefore, accessAfter) \ { \ .Resource = resource, \ diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index a42519f4..d3c19ea4 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -2,8 +2,9 @@ #include "GraphicsTests.h" #include "utest.h" -// TODO: Test Viewports // TODO: Test Multiple render targets +// TODO: Test Multiple Viewports +// TODO: Test Multiple ScissorRectangles // TODO: Check command list type when dispatch mesh // TODO: Multiple config for rendering @@ -161,3 +162,295 @@ UTEST(Rendering, DispatchMesh) ASSERT_LOG_NOERROR(); ASSERT_COLOR_BUFFER(bufferData, 1.0f, 1.0f, 0.0f, 1.0f); } + +UTEST(Rendering, BeginRenderPass_SetViewport) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemViewport renderPassViewport = + { + .X = 2, + .Y = 2, + .Width = 4, + .Height = 4 + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + }, + .Viewports = + { + .Items = &renderPassViewport, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + 2, 2, 4, 4, + 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f); +} + +UTEST(Rendering, BeginRenderPass_SetScissorRectangle) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemRectangle renderPassScissorRectangle = + { + .X = 2, + .Y = 2, + .Width = 4, + .Height = 4 + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + }, + .ScissorRectangles = + { + .Items = &renderPassScissorRectangle, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + 2, 2, 4, 4, + 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f); +} + +UTEST(Rendering, SetViewport) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemViewport viewport = + { + .X = 2, + .Y = 2, + .Width = 4, + .Height = 4 + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemSetViewport(commandList, &viewport); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + 2, 2, 4, 4, + 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f); +} + +UTEST(Rendering, SetScissorRectangle) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + + ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; + ElemGraphicsPipelineStateParameters psoParameters = + { + .RenderTargets = { .Items = &psoRenderTarget, .Length = 1 } + }; + + auto meshShaderPipeline = TestOpenMeshShader(graphicsDevice, "RenderingTests.shader", "MeshShader", "PixelShader", &psoParameters); + + // Act + ElemRenderPassRenderTarget renderPassRenderTarget = + { + .RenderTarget = renderTarget.Texture, + .ClearColor = { .Red = 0.0f, .Green = 0.0f, .Blue = 1.0f, .Alpha = 1.0f }, + .LoadAction = ElemRenderPassLoadAction_Clear + }; + + ElemRectangle scissorRectangle = + { + .X = 2, + .Y = 2, + .Width = 4, + .Height = 4 + }; + + ElemBeginRenderPassParameters parameters = + { + .RenderTargets = + { + .Items = &renderPassRenderTarget, + .Length = 1 + } + }; + + ElemBeginRenderPass(commandList, ¶meters); + ElemBindPipelineState(commandList, meshShaderPipeline); + ElemSetScissorRectangle(commandList, &scissorRectangle); + ElemDispatchMesh(commandList, 1, 1, 1); + ElemEndRenderPass(commandList); + + // Assert + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; + TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + + TestFreeGpuBuffer(readbackBuffer); + TestFreeGpuTexture(renderTarget); + ElemFreePipelineState(meshShaderPipeline); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + 2, 2, 4, 4, + 1.0f, 1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 1.0f, 1.0f); +} From 9c60d44a0336def96047367a7413fc9e13a5001c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 30 Aug 2024 06:11:16 +0200 Subject: [PATCH 24/93] Add mouse wheel to ImGui --- samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl | 3 ++- samples/Elemental/05-HelloImGui/main.c | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl index 3f22a7b3..f55aa7ef 100644 --- a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl +++ b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl @@ -46,7 +46,8 @@ void MeshMain(in uint groupId: SV_GroupID, out vertices VertexOutput vertices[MAX_VERTEX_COUNT], out indices uint3 indices[MAX_VERTEX_COUNT / 3]) { - // TODO: For the moment we process several time the same vertices + // TODO: For the moment we process several time the same vertices because we don't have + // unique indices for the meshlet uint vertexCount = min(MAX_VERTEX_COUNT, parameters.IndexCount - groupId * MAX_VERTEX_COUNT); uint triangleCount = vertexCount / 3; SetMeshOutputCounts(vertexCount, triangleCount); diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index f2dcb24d..0c010759 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -136,6 +136,12 @@ void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParamete { ImGuiIO_AddMouseButtonEvent(imGuiIO, ImGuiMouseButton_Left, inputEvent->Value); } + + if (inputEvent->InputId == ElemInputId_MouseWheelNegative || inputEvent->InputId == ElemInputId_MouseWheelPositive) + { + // TODO: Input wheel should maybe normalized to one? + ImGuiIO_AddMouseWheelEvent(imGuiIO, 0.0f, (inputEvent->InputId == ElemInputId_MouseWheelPositive ? inputEvent->Value : -inputEvent->Value) / 10.0f); + } } //imGuiIO->ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts | ImGuiConfigFlags_DpiEnableScaleViewports; @@ -153,7 +159,6 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl for (int32_t i = 0; i < drawData->CmdListsCount; i++) { - printf("DrawData: Vertexcount = %d, IndexCount=%d, CommandLists=%d\n", drawData->TotalVtxCount, drawData->TotalIdxCount, drawData->CmdLists.Size); ImDrawList* imCommandList = drawData->CmdLists.Data[i]; for (int32_t j = 0; j < imCommandList->CmdBuffer.Size; j++) From 2069acb34f76e93f22b8ea0972412b1eb6e5ede6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 30 Aug 2024 11:19:36 +0200 Subject: [PATCH 25/93] Implement Metal scissor --- .../Apple/Graphics/MetalRendering.cpp | 67 +++++++++++++++---- .../Graphics/Vulkan/VulkanRendering.cpp | 2 + 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/Elemental/Apple/Graphics/MetalRendering.cpp b/src/Elemental/Apple/Graphics/MetalRendering.cpp index 8f3fb82f..01ba8918 100644 --- a/src/Elemental/Apple/Graphics/MetalRendering.cpp +++ b/src/Elemental/Apple/Graphics/MetalRendering.cpp @@ -137,17 +137,39 @@ void MetalBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPass auto textureData = GetMetalResourceData(renderTargetParameters.RenderTarget); SystemAssert(textureData); - ElemViewport viewport = + ElemViewport viewport = { - .X = 0, - .Y = 0, + .X = 0, + .Y = 0, .Width = (float)textureData->Width, .Height = (float)textureData->Height, - .MinDepth = 0.0f, + .MinDepth = 0.0f, .MaxDepth = 1.0f }; - ElemSetViewport(commandList, &viewport); + ElemSetViewport(commandList, &viewport); + } + + if (parameters->ScissorRectangles.Length > 0) + { + ElemSetScissorRectangles(commandList, parameters->ScissorRectangles); + } + else if (parameters->RenderTargets.Length > 0) + { + auto renderTargetParameters = parameters->RenderTargets.Items[0]; + + auto textureData = GetMetalResourceData(renderTargetParameters.RenderTarget); + SystemAssert(textureData); + + ElemRectangle rectangle = + { + .X = 0, + .Y = 0, + .Width = (float)textureData->Width, + .Height = (float)textureData->Height + }; + + ElemSetScissorRectangle(commandList, &rectangle); } } @@ -174,7 +196,6 @@ void MetalSetViewports(ElemCommandList commandList, ElemViewportSpan viewports) auto stackMemoryArena = SystemGetStackMemoryArena(); auto metalViewports = SystemPushArray(stackMemoryArena, viewports.Length); - auto metalScissorRects = SystemPushArray(stackMemoryArena, viewports.Length); for (uint32_t i = 0; i < viewports.Length; i++) { @@ -187,18 +208,40 @@ void MetalSetViewports(ElemCommandList commandList, ElemViewportSpan viewports) .znear = viewports.Items[i].MinDepth, .zfar = viewports.Items[i].MaxDepth }; + } + + renderCommandEncoder->setViewports(metalViewports.Pointer, viewports.Length); +} +void MetalSetScissorRectangles(ElemCommandList commandList, ElemRectangleSpan rectangles) +{ + // TODO: Check command list type != COMPUTE + + SystemAssert(commandList != ELEM_HANDLE_NULL); + + auto commandListData = GetMetalCommandListData(commandList); + SystemAssert(commandListData); + + SystemAssert(commandListData->CommandEncoderType == MetalCommandEncoderType_Render); + SystemAssert(commandListData->CommandEncoder); + + auto renderCommandEncoder = (MTL::RenderCommandEncoder*)commandListData->CommandEncoder.get(); + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto metalScissorRects = SystemPushArray(stackMemoryArena, rectangles.Length); + + for (uint32_t i = 0; i < rectangles.Length; i++) + { metalScissorRects[i] = { - .x = (uint32_t)viewports.Items[i].X, - .y = (uint32_t)viewports.Items[i].Y, - .width = (uint32_t)viewports.Items[i].Width, - .height = (uint32_t)viewports.Items[i].Height, + .x = (uint32_t)rectangles.Items[i].X, + .y = (uint32_t)rectangles.Items[i].Y, + .width = (uint32_t)rectangles.Items[i].Width, + .height = (uint32_t)rectangles.Items[i].Height, }; } - renderCommandEncoder->setViewports(metalViewports.Pointer, viewports.Length); - renderCommandEncoder->setScissorRects(metalScissorRects.Pointer, viewports.Length); + renderCommandEncoder->setScissorRects(metalScissorRects.Pointer, rectangles.Length); } void MetalDispatchMesh(ElemCommandList commandList, uint32_t threadGroupCountX, uint32_t threadGroupCountY, uint32_t threadGroupCountZ) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp index f5380331..5a816f36 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp @@ -129,6 +129,8 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas ElemSetViewport(commandList, &viewport); } + + // TODO: Check this because normally the tests should fail } } From 1b0bd0867789873224dceab2b5168b7dbe3e7cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 31 Aug 2024 06:42:30 +0200 Subject: [PATCH 26/93] Fix vulkan graphics tests --- .../Graphics/Vulkan/VulkanRendering.cpp | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp index 5a816f36..b32cf195 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanRendering.cpp @@ -129,15 +129,21 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas ElemSetViewport(commandList, &viewport); } + + if (i == 0 && parameters->ScissorRectangles.Length == 0) + { + ElemRectangle rectangle = + { + .X = 0, + .Y = 0, + .Width = (float)textureData->Width, + .Height = (float)textureData->Height + }; - // TODO: Check this because normally the tests should fail + ElemSetScissorRectangle(commandList, &rectangle); + } } } - - if (parameters->Viewports.Length > 0) - { - ElemSetViewports(commandList, parameters->Viewports); - } if (parameters->DepthStencil.DepthStencil != ELEM_HANDLE_NULL) { @@ -185,6 +191,16 @@ void VulkanBeginRenderPass(ElemCommandList commandList, const ElemBeginRenderPas EnqueueBarrier(commandListData->ResourceBarrierPool, &resourceBarrier); } + + if (parameters->Viewports.Length > 0) + { + ElemSetViewports(commandList, parameters->Viewports); + } + + if (parameters->ScissorRectangles.Length > 0) + { + ElemSetScissorRectangles(commandList, parameters->ScissorRectangles); + } SystemAssert(renderingWidth != 0 && renderingHeight != 0); From 2ca9349c9d58648a2d2b49274f614f9eee4f3232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 2 Sep 2024 06:04:02 +0200 Subject: [PATCH 27/93] IO Command Queue start --- src/Elemental/Elemental.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 38eaa736..715631f2 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -298,6 +298,8 @@ typedef ElemHandle ElemCommandQueue; */ typedef ElemHandle ElemCommandList; +typedef ElemHandle ElemIOCommandQueue; + /** * Handle that represents a swap chain. */ @@ -352,6 +354,12 @@ typedef enum ElemCommandQueueType_Compute = 1 } ElemCommandQueueType; +typedef enum +{ + ElemIOCommandType_File = 0, + ElemIOCommandType_Memory = 1 +} ElemIOCommandType; + /** * Enumerates swap chain formats. */ @@ -572,6 +580,12 @@ typedef struct const char* DebugName; } ElemCommandListOptions; +typedef struct +{ + // Optional debug name for the command queue. + const char* DebugName; +} ElemIOCommandQueueOptions; + /** * Represents a fence for command list synchronization. */ @@ -614,6 +628,11 @@ typedef struct ElemFenceSpan FencesToWait; } ElemExecuteCommandListOptions; +typedef struct +{ + ElemIOCommandType IOCommandType; +} ElemIOCommandParameters; + /** * Options for configuring a swap chain. */ @@ -996,6 +1015,13 @@ ElemAPI ElemFence ElemExecuteCommandList(ElemCommandQueue commandQueue, ElemComm */ ElemAPI ElemFence ElemExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandListSpan commandLists, const ElemExecuteCommandListOptions* options); +ElemAPI ElemIOCommandQueue ElemCreateIOCommandQueue(const ElemIOCommandQueueOptions* options); +ElemAPI void ElemFreeIOCommandQueue(ElemIOCommandQueue ioQueue); +ElemAPI void ElemEnqueueIOCommand(ElemIOCommandQueue ioCommandQueue, const ElemIOCommandParameters* parameters); +ElemAPI ElemFence ElemSubmitIOCommandQueue(); + +ElemAPI ElemFence ElemExecuteIOCommands(); + /** * Waits for a fence to reach its signaled state on the CPU, effectively synchronizing CPU and GPU operations. * @param fence The fence to wait on. From 1063d84cac9674278c07303e050a5e087cb44a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 10 Sep 2024 04:53:52 +0200 Subject: [PATCH 28/93] Graphics IO exploration --- doc/IOCommands.md | 56 +++++++++++++++++++ .../04-HelloCompute/Data/Fractal.hlsl | 3 +- samples/Elemental/05-HelloImGui/main.c | 3 + samples/Elemental/06-HelloMesh/main.c | 5 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 3 +- src/Elemental/Elemental.h | 3 + 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 doc/IOCommands.md diff --git a/doc/IOCommands.md b/doc/IOCommands.md new file mode 100644 index 00000000..5721aefa --- /dev/null +++ b/doc/IOCommands.md @@ -0,0 +1,56 @@ +# Barriers + +## Decisions to make + +- [x] Do we use the existing CommandQueue/CommandList system? +- [ ] It would be nice to declare just heaps that are always GPU. + For Upload we use copy functions. That handle the copy operation. + For readback we would have only a Readfunction that copy to CPU memory. (Provide a span) + Only problem is vulkan that disallow the creation of VkImages to a ReBar Heap. :( + +## Examples + +1. Solution 1: Separate System + +```c + ElemIOCommandQueue ioCommandQueue = ElemCreateIOCommandQueue(NULL); + + ElemEnqueueIOCommand(&(ElemIOCommandParameters) + { + .DestinationResource = buffer, + .DestinationOffset = 0, + .SourceType = ElemIOCommandSourceType_File, + .SourceFilename = "Test.mesh", + .SourceFileOffset = 500, + .SourceSizeInBytes = 233000, + }); + + ElemFence fence = ElemSubmitIOCommandQueue(); +``` + +2. Solution 2: Reuse current CommandList system + +```c + // This should work with DirectStorage when using IO of with normal queues when using otherwise + // TODO: IO or Transfer or Copy? + ElemCommandQueue ioCommandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_IO, NULL); + ElemCommandList ioCommandList = ElemGetCommandList(ioCommandQueue, NULL); + + // TODO: Simplify? + ElemCopyDataToGraphicsResource(ioCommandList, &(ElemIOCommandParameters) + { + .DestinationResource = buffer, + .DestinationOffset = 0, + .SourceType = ElemIOCommandSourceType_File, + .SourceFilename = "Test.mesh", + .SourceFileOffset = 500, + .SourceSizeInBytes = 233000, + }); + + // Direct upload with re-bar (no commandlist needed) + ElemDirectCopyDataToGraphicsBuffer(); + + ElemCommitCommandList(ioCommandList); + ElemExecuteCommandList(ioCommandQueue, ioCommandList, NULL); + +``` diff --git a/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl b/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl index 903cce78..32fdd6e8 100644 --- a/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl +++ b/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl @@ -2,7 +2,8 @@ struct ShaderParameters { uint32_t RenderTextureIndex; float Zoom; - float3x3 Transform; + float2 Reserved; + float4x4 Transform; }; [[vk::push_constant]] diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 0c010759..2054359f 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -108,6 +108,9 @@ void ImGuiInitBackend(ApplicationPayload* payload) void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters, const ElemInputStream inputStream) { + // TODO: Investigate: https://github.com/ocornut/imgui/releases/tag/v1.91.1 + //ImGuiPlatformIO* imGuiPlatformIO = igGetPlatformIO(); + ImGuiIO* imGuiIO = igGetIO(); imGuiIO->DisplaySize = (ImVec2) diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index 9d0b2477..8418c065 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -101,10 +101,10 @@ void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceD *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; + *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(*buffer); memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); - - *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); } void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) @@ -151,6 +151,7 @@ void InitSample(void* payload) applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues + // TODO: Having GPU Upload is still annoying 😞 applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 9ffb843a..9d2ee07a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -231,7 +231,8 @@ ElemGraphicsHeap VulkanCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uin VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocateInfo.allocationSize = sizeInBytes; - allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; + //allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; + allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuUploadMemoryTypeIndex; if (options) { diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 715631f2..617bcb2a 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -1075,6 +1075,9 @@ ElemAPI ElemGraphicsResourceInfo ElemCreateTexture2DResourceInfo(ElemGraphicsDev ElemAPI ElemGraphicsResource ElemCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, uint64_t graphicsHeapOffset, const ElemGraphicsResourceInfo* resourceInfo); ElemAPI void ElemFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options); ElemAPI ElemGraphicsResourceInfo ElemGetGraphicsResourceInfo(ElemGraphicsResource resource); + +// TODO: Rename taht UploadDataToGraphicsBuffer and pass as a parameter the span in input. We will issue the memcpy, it is too dangerous to manipulate the span +// TODO: We need to make sure then name explain that it is a direct upload without a command list and queue! ElemAPI ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource resource); ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); From 8b9966704cc29ed60e14211f586446643d1019d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 19 Sep 2024 05:25:50 +0200 Subject: [PATCH 29/93] WIP SwitchPro Controller --- samples/Common/SampleInputs.h | 2 + samples/Elemental/06-HelloMesh/main.c | 35 ++- src/Elemental/Apple/PreCompiledHeader.h | 1 - src/Elemental/Common/Inputs/HidDevices.cpp | 217 ++++++++++++--- src/Elemental/Common/Inputs/HidDevices.h | 2 +- .../Common/Inputs/HidSwitchGamepad.h | 257 ++++++++++++++++++ src/Elemental/Common/Inputs/HidUtils.h | 27 ++ src/Elemental/Common/Inputs/Inputs.cpp | 44 +-- src/Elemental/Common/Inputs/Inputs.h | 10 + src/Elemental/Elemental.h | 4 + src/Elemental/Linux/PreCompiledHeader.h | 1 - src/Elemental/Microsoft/PreCompiledHeader.h | 1 - src/Elemental/Microsoft/Win32Inputs.cpp | 88 ++++++ 13 files changed, 622 insertions(+), 67 deletions(-) create mode 100644 src/Elemental/Common/Inputs/HidSwitchGamepad.h create mode 100644 src/Elemental/Common/Inputs/HidUtils.h diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index 41b77c6b..fed9cb94 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -191,6 +191,7 @@ void SampleRegisterStandardInputBindings(SampleInputActionBindingSpan* inputActi SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->Action1); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputActions->Touch); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchTranslateLeft); @@ -242,6 +243,7 @@ void SampleRegisterModelViewerInputBindings(SampleInputActionBindingSpan* inputA SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputActions->Touch); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateLeft); diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index 8418c065..f6381456 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -51,10 +51,16 @@ typedef struct ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; } MeshData; -// TODO: Group common variables into separate structs typedef struct { bool PreferVulkan; + bool PreferFullScreen; +} SampleAppSettings; + +// TODO: Group common variables into separate structs +typedef struct +{ + SampleAppSettings AppSettings; ElemWindow Window; ElemGraphicsDevice GraphicsDevice; ElemCommandQueue CommandQueue; @@ -138,9 +144,9 @@ void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicat void InitSample(void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - applicationPayload->Window = ElemCreateWindow(NULL); + applicationPayload->Window = ElemCreateWindow(&(ElemWindowOptions) { .WindowState = applicationPayload->AppSettings.PreferFullScreen ? ElemWindowState_FullScreen : ElemWindowState_Normal }); - ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->PreferVulkan }); + ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); @@ -157,7 +163,7 @@ void InitSample(void* payload) CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); LoadMesh(&applicationPayload->TestMeshData, "kitten.mesh", applicationPayload); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { @@ -391,20 +397,29 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void int main(int argc, const char* argv[]) { - bool preferVulkan = false; + // TODO: Extract arguments parsing code to sample + SampleAppSettings appSettings = {}; - if (argc > 1 && strcmp(argv[1], "--vulkan") == 0) + for (int32_t i = 1; i < argc; i++) { - preferVulkan = true; - } + if (strcmp(argv[i], "--vulkan") == 0) + { + appSettings.PreferVulkan = true; + } - ElemConfigureLogHandler(ElemConsoleLogHandler); + if (strcmp(argv[i], "--fullscreen") == 0) + { + appSettings.PreferFullScreen = true; + } + } ApplicationPayload payload = { - .PreferVulkan = preferVulkan + .AppSettings = appSettings }; + ElemConfigureLogHandler(ElemConsoleLogHandler); + ElemRunApplication(&(ElemRunApplicationParameters) { .ApplicationName = "Hello Mesh", diff --git a/src/Elemental/Apple/PreCompiledHeader.h b/src/Elemental/Apple/PreCompiledHeader.h index 4c563f45..79eedf20 100644 --- a/src/Elemental/Apple/PreCompiledHeader.h +++ b/src/Elemental/Apple/PreCompiledHeader.h @@ -30,7 +30,6 @@ typedef signed int HRESULT; #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define ElemAPI extern "C" __attribute__((visibility("default"))) -#define PackedStruct struct __attribute__((__packed__)) #define AssertIfFailed(expression) SystemAssert(SUCCEEDED((expression))) #define AssertIfFailedReturnNullHandle(expression) SystemAssertReturnNullHandle(SUCCEEDED((expression))) #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) diff --git a/src/Elemental/Common/Inputs/HidDevices.cpp b/src/Elemental/Common/Inputs/HidDevices.cpp index 9f9386dd..c202658f 100644 --- a/src/Elemental/Common/Inputs/HidDevices.cpp +++ b/src/Elemental/Common/Inputs/HidDevices.cpp @@ -1,11 +1,14 @@ #include "HidDevices.h" +#include "HidUtils.h" +#include "HidSwitchGamepad.h" #include "Inputs.h" #include "SystemFunctions.h" enum HidGamepadVendor : uint32_t { HidGamepadVendor_Microsoft = 0x045E, - HidGamepadVendor_Sony = 0x054C + HidGamepadVendor_Sony = 0x054C, + HidGamepadVendor_Nintendo = 0x057E }; enum HidGamepadProduct : uint32_t @@ -14,19 +17,21 @@ enum HidGamepadProduct : uint32_t HidGamepadProduct_XboxOneUsb = 0x02FF, HidGamepadProduct_XboxOneWireless = 0x02FD, HidGamepadProduct_DualShock4OldDriver = 0x5C4, - HidGamepadProduct_DualShock4 = 0x9cc + HidGamepadProduct_DualShock4 = 0x9cc, + HidGamepadProduct_DualSense = 0x0ce6, + HidGamepadProduct_SwitchPro = 0x2009 }; -typedef void (*ProcessHidGamepadDataPtr)(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds); +typedef void (*ProcessHidGamepadInputReportPtr)(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds); struct HidGamepadHandler { HidGamepadVendor Vendor; HidGamepadProduct Product; - ProcessHidGamepadDataPtr ProcessDataHandler; + ProcessHidGamepadInputReportPtr ProcessDataHandler; }; -PackedStruct XboxOneWirelessOldDriverGamepadReport +struct __attribute__((__packed__)) XboxOneWirelessOldDriverGamepadReport { uint8_t Padding; uint16_t LeftStickX; @@ -38,7 +43,7 @@ PackedStruct XboxOneWirelessOldDriverGamepadReport uint8_t Dpad; }; -PackedStruct XboxOneWirelessGamepadReport +struct __attribute__((__packed__)) XboxOneWirelessGamepadReport { uint8_t Padding; uint16_t LeftStickX; @@ -51,31 +56,17 @@ PackedStruct XboxOneWirelessGamepadReport uint32_t Buttons; }; -float NormalizeInputValue(uint32_t value, uint32_t maxValue, float deadZone) +struct __attribute__((__packed__)) DualSenseSimpleGamepadReport { - // TODO: Allows the configuration of deadzone - float normalizedValue = ((float)value / (float)maxValue); - - if (normalizedValue < deadZone) - { - return 0.0f; - } - - return normalizedValue; -} - -float NormalizeInputValueSigned(uint32_t value, uint32_t maxValue, float deadZone) -{ - // TODO: Allows the configuration of deadzone - float normalizedValue = ((float)value / (float)maxValue) * 2.0f - 1.0f; - - if (normalizedValue < deadZone && normalizedValue > -deadZone) - { - return 0.0f; - } - - return normalizedValue; -} + uint8_t LeftStickX; + uint8_t LeftStickY; + uint16_t RightStickX; + uint16_t RightStickY; + uint16_t LeftTrigger; + uint16_t RightTrigger; + uint8_t Dpad; + uint32_t Buttons; +}; void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) { @@ -163,6 +154,15 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadButtonB, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); AddInputEvent({ .Window = window, @@ -290,6 +290,15 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe .ElapsedSeconds = elapsedSeconds }); + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadButtonB, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + AddInputEvent({ .Window = window, .InputDevice = inputDevice, @@ -309,11 +318,156 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe }); } +void ProcessDualSenseGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + auto reportId = hidReport[0]; + + if (reportId == 1) + { + auto inputReport = (DualSenseSimpleGamepadReport*)(++hidReport.Pointer); + + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Process DualSense %d %d", reportId, inputReport->LeftStickX); + + // TODO: Also here we need to send the event only if the analog value has changed + // TODO: We can refactor this function by taking the sizeof of the field? + float leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, 256, 0.2f); + + if (leftStickX <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (leftStickX >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + } +/* + float leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, 65535, 0.2f); + + if (leftStickY <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + // TODO: check if the value is inversed!! + if (leftStickY >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + + const uint16_t midpoint = 32768; + + uint8_t left, right; + + // Calculate the right trigger value as a difference from midpoint + if (inputReport->Triggers <= midpoint) { + right = static_cast((midpoint - inputReport->Triggers) >> 7); + } else { + right = 0; + } + + // Calculate the left trigger value as a difference from midpoint + if (inputReport->Triggers >= midpoint) { + left = static_cast((inputReport->Triggers - midpoint) >> 7); + } else { + left = 0; + } + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadLeftTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(left, 255, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadRightTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(right, 255, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + // TODO: For buttons we need to keep track of old report to see if there was a change + // Maybe we can do the same as with delta inputs! + + + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadButtonA, + .InputType = ElemInputType_Digital, + .Value = (inputReport->Buttons & 0x01) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadButtonB, + .InputType = ElemInputType_Digital, + .Value = (inputReport->Buttons & 0x02) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadLeftShoulder, + .InputType = ElemInputType_Digital, + .Value = (inputReport->Buttons & 0x10) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputID_GamepadRightShoulder, + .InputType = ElemInputType_Digital, + .Value = (inputReport->Buttons & 0x20) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + });*/ +} + HidGamepadHandler HidGamepadHandlers[] = { { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWirelessOldDriver, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneUsb, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, - { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWireless, .ProcessDataHandler = ProcessXboxOneWirelessGamepadReport } + { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWireless, .ProcessDataHandler = ProcessXboxOneWirelessGamepadReport }, + { .Vendor = HidGamepadVendor_Sony, .Product = HidGamepadProduct_DualSense, .ProcessDataHandler = ProcessDualSenseGamepadReport }, + { .Vendor = HidGamepadVendor_Nintendo, .Product = HidGamepadProduct_SwitchPro, .ProcessDataHandler = ProcessHidSwitchGamepadInputReport } }; bool IsHidDeviceSupported(uint32_t vendorId, uint32_t productId) @@ -330,6 +484,7 @@ bool IsHidDeviceSupported(uint32_t vendorId, uint32_t productId) return false; } + void ProcessHidDeviceData(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) { auto inputDeviceData = GetInputDeviceData(inputDevice); diff --git a/src/Elemental/Common/Inputs/HidDevices.h b/src/Elemental/Common/Inputs/HidDevices.h index 0ee6a0f2..3b55ea40 100644 --- a/src/Elemental/Common/Inputs/HidDevices.h +++ b/src/Elemental/Common/Inputs/HidDevices.h @@ -1,6 +1,6 @@ #pragma once -#include "Elemental.h" +#include "../Elemental.h" #include "SystemSpan.h" bool IsHidDeviceSupported(uint32_t vendorId, uint32_t productId); diff --git a/src/Elemental/Common/Inputs/HidSwitchGamepad.h b/src/Elemental/Common/Inputs/HidSwitchGamepad.h new file mode 100644 index 00000000..e32b54c0 --- /dev/null +++ b/src/Elemental/Common/Inputs/HidSwitchGamepad.h @@ -0,0 +1,257 @@ +#pragma once + +#include "../Elemental.h" +#include "Inputs.h" +#include "HidUtils.h" +#include "SystemFunctions.h" +#include "SystemDataPool.h" + +// Based on the spec: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md + +#define HID_SWITCH_STICK_RANGE 65535 +#define HID_SWITCH_STICK_DEAD_ZONE 0.2f + +enum HidSwitchGamepadInputReportType +{ + HidSwitchGamepadInputReportType_Standard = 0x3F, + HidSwitchGamepadInputReportType_SubCommand = 0x21 +}; + +enum HidSwitchGamepadOutputReportType +{ + HidSwitchGamepadOutputReportType_SubCommand = 0x01 +}; + +enum HidSwitchGamepadSubCommandType : uint8_t +{ + HidSwitchGamepadSubCommandType_DeviceInfo = 0x02 +}; + +enum HidSwitchGamepadControllerType : uint8_t +{ + HidSwitchGamepadControllerType_LeftJoyCon = 0x01, + HidSwitchGamepadControllerType_RightJoyCon = 0x02, + HidSwitchGamepadControllerType_ProController = 0x03, +}; + +struct HidSwitchGamepadData +{ + uint8_t CurrentOutputPacketNumber; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadInputReportStandard +{ + uint8_t Buttons[2]; + uint8_t Dpad; + uint16_t LeftStickX; + uint16_t LeftStickY; + uint16_t RightStickX; + uint16_t RightStickY; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadInputReportExtended +{ + uint8_t Timer; + uint8_t BatteryConnection; + uint8_t Buttons[3]; + uint8_t LeftStick[3]; + uint8_t RightStick[3]; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadInputReportDeviceInfo +{ + uint16_t FirmwareVersion; + HidSwitchGamepadControllerType ControllerType; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadOutputReportSubCommand +{ + uint8_t ReportId; + uint8_t GlobalPacketNumber; + // TODO: Rumble Data + uint8_t RumbleData[8]; + HidSwitchGamepadSubCommandType SubCommandId; + uint8_t Data[53]; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadFeatureReportSetupMemoryRead +{ + uint8_t ReportId; + uint32_t Address; + uint16_t Size; + uint8_t Checksum; +}; + +struct __attribute__((__packed__)) HidSwitchGamepadFeatureReportMemoryRead +{ + uint8_t ReportId; + uint8_t Checksum; + uint8_t Data[1024]; +}; + +static SystemDataPool hidSwitchGamepadDataPool; + +inline void InitHidSwitchGamepadMemory() +{ + if (hidSwitchGamepadDataPool.Storage == nullptr) + { + hidSwitchGamepadDataPool = SystemCreateDataPool(InputsMemoryArena, MAX_INPUT_DEVICES); + } +} + +inline HidSwitchGamepadData* GetHidSwitchGamepadData(ElemHandle hidDevice) +{ + return SystemGetDataPoolItem(hidSwitchGamepadDataPool, hidDevice); +} + +inline bool SendHidSwitchGamepadSubCommand(ElemInputDevice inputDevice, HidSwitchGamepadSubCommandType subCommandType, ReadOnlySpan data) +{ + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + auto hidData = GetHidSwitchGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + HidSwitchGamepadOutputReportSubCommand outputReport = + { + .ReportId = HidSwitchGamepadOutputReportType_SubCommand, + .GlobalPacketNumber = hidData->CurrentOutputPacketNumber, + .SubCommandId = subCommandType + }; + + hidData->CurrentOutputPacketNumber = (hidData->CurrentOutputPacketNumber + 1) % 255; + + return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); +} + +inline void ProcessHidSwitchGamepadInputReportStandard(ElemWindow window, ElemInputDevice inputDevice, HidSwitchGamepadInputReportStandard* inputReport, double elapsedSeconds) +{ + // TODO: Also here we need to send the event only if the analog value has changed + float leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, HID_SWITCH_STICK_RANGE, HID_SWITCH_STICK_DEAD_ZONE); + + if (leftStickX <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (leftStickX >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + float leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, HID_SWITCH_STICK_RANGE, HID_SWITCH_STICK_DEAD_ZONE); + + if (leftStickY <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (leftStickY >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + + // TODO: TESTING ONLY + if (inputReport->Buttons[0] & 0x01) + { + SendHidSwitchGamepadSubCommand(inputDevice, HidSwitchGamepadSubCommandType_DeviceInfo, ReadOnlySpan()); + } +} + +inline void ProcessHidSwitchGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + InitHidSwitchGamepadMemory(); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + if (inputDeviceData->HidDeviceData == ELEM_HANDLE_NULL) + { + // Read Config + HidSwitchGamepadFeatureReportSetupMemoryRead featureReport = + { + .ReportId = 0x71, + .Address = 0xF8000000 + 0x6000, + .Size = 0xA000, + .Checksum = 0 + }; + + auto pointer = (uint8_t*)&featureReport; + auto sumBytes = 0u; + + for (uint32_t i = 0; i < sizeof(featureReport); i++) + { + sumBytes += pointer[i]; + } + + featureReport.Checksum = 0x100 - sumBytes; + + auto result = PlatformHidSendFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&featureReport, sizeof(featureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Send Feature Result: %d", result); + + HidSwitchGamepadFeatureReportMemoryRead readFeatureReport = + { + .ReportId = 0x72, + .Checksum = 0 + }; + + result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&readFeatureReport, sizeof(readFeatureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result); + + inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidSwitchGamepadDataPool, {}); + } + + auto reportId = hidReport[0]; + + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "SwitchGamePad %d", reportId); + + // TODO: Use the the advanced report + if (reportId == HidSwitchGamepadInputReportType_Standard) + { + auto inputReport = (HidSwitchGamepadInputReportStandard*)(++hidReport.Pointer); + ProcessHidSwitchGamepadInputReportStandard(window, inputDevice, inputReport, elapsedSeconds); + } + else if (reportId == HidSwitchGamepadInputReportType_SubCommand) + { + auto hidData = GetHidSwitchGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + + auto inputReport = (HidSwitchGamepadInputReportExtended*)(++hidReport.Pointer); + + uint8_t *data = inputReport->LeftStick; + uint16_t stick_horizontal = data[0] | ((data[1] & 0xF) << 8); + uint16_t stick_vertical = (data[1] >> 4) | (data[2] << 4); + + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Report: Timer=%d, X=%d, Y=%d", inputReport->Timer, stick_horizontal, stick_vertical); + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); + } +} diff --git a/src/Elemental/Common/Inputs/HidUtils.h b/src/Elemental/Common/Inputs/HidUtils.h new file mode 100644 index 00000000..f8c3ec76 --- /dev/null +++ b/src/Elemental/Common/Inputs/HidUtils.h @@ -0,0 +1,27 @@ +#pragma once + +inline float NormalizeInputValue(uint32_t value, uint32_t maxValue, float deadZone) +{ + // TODO: Allows the configuration of deadzone + float normalizedValue = ((float)value / (float)maxValue); + + if (normalizedValue < deadZone) + { + return 0.0f; + } + + return normalizedValue; +} + +inline float NormalizeInputValueSigned(uint32_t value, uint32_t maxValue, float deadZone) +{ + // TODO: Allows the configuration of deadzone + float normalizedValue = ((float)value / (float)maxValue) * 2.0f - 1.0f; + + if (normalizedValue < deadZone && normalizedValue > -deadZone) + { + return 0.0f; + } + + return normalizedValue; +} diff --git a/src/Elemental/Common/Inputs/Inputs.cpp b/src/Elemental/Common/Inputs/Inputs.cpp index 6102721b..93f23dc0 100644 --- a/src/Elemental/Common/Inputs/Inputs.cpp +++ b/src/Elemental/Common/Inputs/Inputs.cpp @@ -5,7 +5,7 @@ #include "SystemMemory.h" #include "SystemPlatformFunctions.h" -MemoryArena inputsMemoryArena; +MemoryArena InputsMemoryArena; Span inputEvents; uint32_t currentInputEventsIndex; uint32_t currentInputEventsWriteIndex; @@ -22,14 +22,14 @@ SystemDataPool inputDevicePool; void InitInputsMemory() { - if (inputsMemoryArena.Storage == nullptr) + if (InputsMemoryArena.Storage == nullptr) { - inputsMemoryArena = SystemAllocateMemoryArena(); - inputEvents = SystemPushArray(inputsMemoryArena, MAX_INPUT_EVENTS * 2); - deltaInputsToReset = SystemPushArrayZero(inputsMemoryArena, MAX_INPUT_EVENTS * 2); + InputsMemoryArena = SystemAllocateMemoryArena(); + inputEvents = SystemPushArray(InputsMemoryArena, MAX_INPUT_EVENTS * 2); + deltaInputsToReset = SystemPushArrayZero(InputsMemoryArena, MAX_INPUT_EVENTS * 2); - inputDevices = SystemPushArrayZero(inputsMemoryArena, MAX_INPUT_DEVICES); - inputDevicePool = SystemCreateDataPool(inputsMemoryArena, MAX_INPUT_DEVICES); + inputDevices = SystemPushArrayZero(InputsMemoryArena, MAX_INPUT_DEVICES); + inputDevicePool = SystemCreateDataPool(InputsMemoryArena, MAX_INPUT_DEVICES); currentInputEventsIndex = 0; currentInputEventsWriteIndex = 0; @@ -107,21 +107,6 @@ void AddInputEvent(ElemInputEvent inputEvent, bool needReset) inputEvents[currentInputEventsIndex * MAX_INPUT_EVENTS + currentInputEventsWriteIndex++] = inputEvent; } -ElemAPI ElemInputDeviceInfo ElemGetInputDeviceInfo(ElemInputDevice inputDevice) -{ - auto inputDeviceData = GetInputDeviceData(inputDevice); - SystemAssert(inputDeviceData); - - auto inputDeviceDataFull = GetInputDeviceDataFull(inputDevice); - SystemAssert(inputDeviceDataFull); - - return - { - .Handle = inputDevice, - .DeviceType = inputDeviceData->InputDeviceType, - }; -} - void ResetInputsFrame() { InitInputsMemory(); @@ -148,6 +133,21 @@ void ResetInputsFrame() } } +ElemAPI ElemInputDeviceInfo ElemGetInputDeviceInfo(ElemInputDevice inputDevice) +{ + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData); + + auto inputDeviceDataFull = GetInputDeviceDataFull(inputDevice); + SystemAssert(inputDeviceDataFull); + + return + { + .Handle = inputDevice, + .DeviceType = inputDeviceData->InputDeviceType, + }; +} + ElemAPI ElemInputStream ElemGetInputStream() { InitInputsMemory(); diff --git a/src/Elemental/Common/Inputs/Inputs.h b/src/Elemental/Common/Inputs/Inputs.h index 87146c90..4b69643f 100644 --- a/src/Elemental/Common/Inputs/Inputs.h +++ b/src/Elemental/Common/Inputs/Inputs.h @@ -1,6 +1,7 @@ #pragma once #include "../Elemental.h" +#include "SystemMemory.h" #define MAX_INPUT_DEVICES 64u #define MAX_INPUT_EVENTS 1024u @@ -11,6 +12,8 @@ struct InputDeviceData ElemInputDeviceType InputDeviceType; uint32_t HidVendorId; uint32_t HidProductId; + void* PlatformData; + ElemHandle HidDeviceData; }; struct InputDeviceDataFull @@ -18,6 +21,8 @@ struct InputDeviceDataFull uint32_t reserved; }; +extern MemoryArena InputsMemoryArena; + InputDeviceData* GetInputDeviceData(ElemInputDevice inputDevice); InputDeviceDataFull* GetInputDeviceDataFull(ElemInputDevice inputDevice); @@ -26,3 +31,8 @@ void RemoveInputDevice(ElemInputDevice inputDevice); void AddInputEvent(ElemInputEvent inputEvent, bool needReset = false); void ResetInputsFrame(); + +bool PlatformHidSendOutputReport(ElemInputDevice inputDevice, ReadOnlySpan data); +bool PlatformHidSendFeatureReport(ElemInputDevice inputDevice, ReadOnlySpan data); +bool PlatformHidGetFeatureReport(ElemInputDevice inputDevice, ReadOnlySpan data); + diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 617bcb2a..3bede491 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -184,6 +184,8 @@ typedef struct float UIScale; // Current state of the window. ElemWindowState WindowState; + // TODO: Return refresh rate + // TODO: Return an ElemMonitor so that we can get the info about the monitor } ElemWindowSize; // TODO: Comments @@ -273,6 +275,8 @@ ElemAPI void ElemSetWindowTitle(ElemWindow window, const char* title); */ ElemAPI void ElemSetWindowState(ElemWindow window, ElemWindowState windowState); +// TODO: Function to get monitor infos + // TODO: Comments // TODO: Make sure the coordinates are consistent accross all platforms ElemAPI void ElemShowWindowCursor(ElemWindow window); diff --git a/src/Elemental/Linux/PreCompiledHeader.h b/src/Elemental/Linux/PreCompiledHeader.h index 84bb345e..bafde8f7 100644 --- a/src/Elemental/Linux/PreCompiledHeader.h +++ b/src/Elemental/Linux/PreCompiledHeader.h @@ -33,7 +33,6 @@ typedef signed int HRESULT; #define SUCCEEDED(Status) ((HRESULT)(Status) >= 0) #define ElemAPI extern "C" __attribute__((visibility("default"))) -#define PackedStruct struct __attribute__((__packed__)) #define AssertIfFailed(expression) SystemAssert(SUCCEEDED((expression))) #define AssertIfFailedReturnNullHandle(expression) SystemAssertReturnNullHandle(SUCCEEDED((expression))) #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) diff --git a/src/Elemental/Microsoft/PreCompiledHeader.h b/src/Elemental/Microsoft/PreCompiledHeader.h index bc0ea493..1fc7654c 100644 --- a/src/Elemental/Microsoft/PreCompiledHeader.h +++ b/src/Elemental/Microsoft/PreCompiledHeader.h @@ -18,7 +18,6 @@ #include -#define PackedStruct struct __attribute__((__packed__)) #define ElemAPI extern "C" __declspec(dllexport) #define AssertIfFailed(expression) SystemAssert(SUCCEEDED((expression))) #define AssertIfFailedReturnNullHandle(expression) SystemAssertReturnNullHandle(SUCCEEDED((expression))) diff --git a/src/Elemental/Microsoft/Win32Inputs.cpp b/src/Elemental/Microsoft/Win32Inputs.cpp index 6ad386c5..593c9572 100644 --- a/src/Elemental/Microsoft/Win32Inputs.cpp +++ b/src/Elemental/Microsoft/Win32Inputs.cpp @@ -516,3 +516,91 @@ void ProcessWin32RawInput(ElemWindow window, LPARAM lParam) ProcessHidDeviceData(window, inputDevice, ReadOnlySpan(rawInputData->data.hid.bRawData, rawInputData->data.hid.dwSizeHid), elapsedSeconds); } } + +void CreateWin32HidHandleIfNeeded(ElemInputDevice inputDevice) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData); + + if (!inputDeviceData->PlatformData) + { + uint32_t inputDevicePathSize; + AssertIfFailed(GetRawInputDeviceInfo(inputDeviceData->PlatformHandle, RIDI_DEVICENAME, nullptr, &inputDevicePathSize)); + + auto rawInputDeviceInfo = SystemPushArray(stackMemoryArena, inputDevicePathSize).Pointer; + + AssertIfFailed(GetRawInputDeviceInfo(inputDeviceData->PlatformHandle, RIDI_DEVICENAME, rawInputDeviceInfo, &inputDevicePathSize)); + + auto inputDeviceHandle = CreateFile(rawInputDeviceInfo, + GENERIC_WRITE | GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + 0, + nullptr); + + if (inputDeviceHandle == INVALID_HANDLE_VALUE) + { + SystemLogErrorMessage(ElemLogMessageCategory_Inputs, "Cannot get hid device."); + return; + } + + inputDeviceData->PlatformData = inputDeviceHandle; + } +} + +bool PlatformHidSendOutputReport(ElemInputDevice inputDevice, ReadOnlySpan data) +{ + CreateWin32HidHandleIfNeeded(inputDevice); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData); + + auto success = WriteFile(inputDeviceData->PlatformData, data.Pointer, data.Length, nullptr, nullptr); + + if (!success) + { + SystemLogErrorMessage(ElemLogMessageCategory_Inputs, "Error Code: %d", GetLastError()); + return false; + } + + return true; +} + +bool PlatformHidSendFeatureReport(ElemInputDevice inputDevice, ReadOnlySpan data) +{ + CreateWin32HidHandleIfNeeded(inputDevice); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData); + + auto success = HidD_SetFeature(inputDeviceData->PlatformData, (void*)data.Pointer, data.Length); + + if (!success) + { + SystemLogErrorMessage(ElemLogMessageCategory_Inputs, "Error Code: %d", GetLastError()); + return false; + } + + return true; +} + +bool PlatformHidGetFeatureReport(ElemInputDevice inputDevice, ReadOnlySpan data) +{ + CreateWin32HidHandleIfNeeded(inputDevice); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData); + + auto success = HidD_GetFeature(inputDeviceData->PlatformData, (void*)data.Pointer, data.Length); + + if (!success) + { + SystemLogErrorMessage(ElemLogMessageCategory_Inputs, "Error Code: %d", GetLastError()); + return false; + } + + return true; +} From 9fc48712374b09980f3b424ad62e6a121cfd8d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 8 Nov 2024 07:42:41 +0100 Subject: [PATCH 30/93] Refactor samples and inputs --- samples/Common/SampleApplicationInputs.h | 61 ++++ samples/Common/SampleInputs.h | 89 +---- samples/Common/SampleModelViewerInputs.h | 209 ++++++++++++ samples/Common/SampleUtils.h | 31 ++ samples/Elemental/04-HelloCompute/main.c | 20 +- .../06-HelloMesh/Data/RenderMesh.hlsl | 4 +- samples/Elemental/06-HelloMesh/main.c | 227 +++---------- src/Elemental/Common/Inputs/HidDevices.cpp | 8 +- .../Common/Inputs/HidDualSenseGamepad.h | 309 ++++++++++++++++++ src/Elemental/Elemental.h | 7 +- .../Graphics/DirectX12GraphicsDevice.cpp | 2 + .../Microsoft/Graphics/DirectX12SwapChain.cpp | 4 + src/Elemental/Microsoft/Win32Window.cpp | 1 + 13 files changed, 701 insertions(+), 271 deletions(-) create mode 100644 samples/Common/SampleApplicationInputs.h create mode 100644 samples/Common/SampleModelViewerInputs.h create mode 100644 src/Elemental/Common/Inputs/HidDualSenseGamepad.h diff --git a/samples/Common/SampleApplicationInputs.h b/samples/Common/SampleApplicationInputs.h new file mode 100644 index 00000000..45b72438 --- /dev/null +++ b/samples/Common/SampleApplicationInputs.h @@ -0,0 +1,61 @@ +#pragma once + +#include "Elemental.h" +#include "SampleInputs.h" + +typedef struct +{ + float SwitchShowCursor; + float ExitApp; +} SampleApplicationInputActions; + +typedef struct +{ + bool ExitApplication; + bool ShowCursor; + bool HideCursor; + bool IsCursorDisplayed; +} SampleApplicationInputState; + +typedef struct +{ + SampleApplicationInputActions InputActions; + SampleInputActionBindingSpan InputActionBindings; + SampleApplicationInputState State; +} SampleApplicationInputs; + +void SampleApplicationInputsInit(SampleApplicationInputs* inputs) +{ + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_Released, &inputs->InputActions.SwitchShowCursor); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); + + inputs->State.IsCursorDisplayed = true; +} + +void SampleApplicationInputsUpdate(ElemInputStream inputStream, SampleApplicationInputs* inputs, float deltaTimeInSeconds) +{ + SampleApplicationInputState* state = &inputs->State; + SampleApplicationInputActions* inputActions = &inputs->InputActions; + + SampleUpdateInputActions(&inputs->InputActionBindings, inputStream); + + state->ExitApplication = inputActions->ExitApp; + state->ShowCursor = false; + state->HideCursor = false; + + if (inputActions->SwitchShowCursor) + { + if (!state->IsCursorDisplayed) + { + state->ShowCursor = true; + } + else + { + state->HideCursor = true; + } + + state->IsCursorDisplayed = !state->IsCursorDisplayed; + } +} diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index fed9cb94..20498a07 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -26,6 +26,7 @@ typedef struct uint32_t Length; } SampleInputActionBindingSpan; +// TODO: Extract ModelView into a separate file typedef struct { float TranslateLeft; @@ -52,40 +53,10 @@ typedef struct float TouchRotateSide; float Action1; - float ShowCursor; + float SwitchShowCursor; float ExitApp; } SampleStandardInputActions; -typedef struct -{ - float RotateLeft; - float RotateRight; - float RotateUp; - float RotateDown; - float RotateSideLeft; - float RotateSideRight; - float ZoomIn; - float ZoomOut; - - float Touch; - float TouchReleased; - float TouchRotateLeft; - float TouchRotateRight; - float TouchRotateUp; - float TouchRotateDown; - float TouchPositionX; - float TouchPositionY; - - float Touch2; - float Touch2PositionX; - float Touch2PositionY; - - float TouchRotateSide; - float TriangleColor; - float ShowCursor; - float ExitApp; -} SampleModelViewerInputActions; - void SampleRegisterInputActionBinding(SampleInputActionBindingSpan* bindings, ElemInputId inputId, uint32_t index, SampleInputActionBindingType bindingType, float* actionValue) { bindings->Items[bindings->Length++] = (SampleInputActionBinding) @@ -113,6 +84,8 @@ void SampleUpdateInputActions(SampleInputActionBindingSpan* inputActionBindings, { ElemInputEvent* inputEvent = &inputStream.Events.Items[i]; + printf("Input Event: %d, %f\n", inputEvent->InputId, inputEvent->Value); + for (uint32_t j = 0; j < inputActionBindings->Length; j++) { SampleInputActionBinding* binding = &inputActionBindings->Items[j]; @@ -166,7 +139,7 @@ void SampleRegisterStandardInputBindings(SampleInputActionBindingSpan* inputActi SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyZ, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyX, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputActions->Action1); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_ReleasedSwitch, &inputActions->ShowCursor); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_ReleasedSwitch, &inputActions->SwitchShowCursor); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Value, &inputActions->Touch); @@ -207,55 +180,3 @@ void SampleRegisterStandardInputBindings(SampleInputActionBindingSpan* inputActi SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->Action1); } -void SampleRegisterModelViewerInputBindings(SampleInputActionBindingSpan* inputActionBindings, SampleModelViewerInputActions* inputActions) -{ - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyA, 0, SampleInputActionBindingType_Value, &inputActions->RotateLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyD, 0, SampleInputActionBindingType_Value, &inputActions->RotateRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyW, 0, SampleInputActionBindingType_Value, &inputActions->RotateUp); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyS, 0, SampleInputActionBindingType_Value, &inputActions->RotateDown); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyQ, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyE, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyZ, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyX, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_ReleasedSwitch, &inputActions->ShowCursor); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); - - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Value, &inputActions->Touch); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->TriangleColor); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Released, &inputActions->TouchReleased); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseRightButton, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateSide); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisXPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisYNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateUp); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateDown); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseWheelPositive, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseWheelNegative, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_MouseMiddleButton, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); - - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputActions->RotateLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputActions->RotateRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputActions->RotateUp); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputActions->RotateDown); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickButton, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->TriangleColor); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); - - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputActions->Touch); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateUp); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYPositive, 0, SampleInputActionBindingType_Value, &inputActions->TouchRotateDown); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputActions->TouchPositionX); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputActions->TouchPositionY); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 1, SampleInputActionBindingType_Value, &inputActions->Touch2); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputActions->Touch2PositionX); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchYAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputActions->Touch2PositionY); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Released, &inputActions->TouchReleased); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputActions->TriangleColor); -} - diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleModelViewerInputs.h new file mode 100644 index 00000000..df5e3003 --- /dev/null +++ b/samples/Common/SampleModelViewerInputs.h @@ -0,0 +1,209 @@ +#pragma once + +#include "Elemental.h" +#include "SampleInputs.h" +#include "SampleMath.h" + +#define SAMPLE_MODELVIEWER_ROTATION_TOUCH_DECREASE_SPEED 0.001f +#define SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED 4.0f +#define SAMPLE_MODELVIEWER_ROTATION_TOUCH_MAX_DELTA 0.3f +#define SAMPLE_MODELVIEWER_ROTATION_MULTITOUCH_SPEED 200.0f +#define SAMPLE_MODELVIEWER_ROTATION_ACCELERATION 500.0f +#define SAMPLE_MODELVIEWER_ROTATION_FRICTION 60.0f +#define SAMPLE_MODELVIEWER_ROTATION_ZOOM_MULTITOUCH_SPEED 1000.0f +#define SAMPLE_MODELVIEWER_ROTATION_ZOOM_SPEED 5.0f + +typedef struct +{ + float RotateLeft; + float RotateRight; + float RotateUp; + float RotateDown; + float RotateSideLeft; + float RotateSideRight; + float ZoomIn; + float ZoomOut; + + float Touch; + float TouchReleased; + float TouchRotateLeft; + float TouchRotateRight; + float TouchRotateUp; + float TouchRotateDown; + float TouchPositionX; + float TouchPositionY; + + float Touch2; + float Touch2PositionX; + float Touch2PositionY; + + float TouchRotateSide; + float Action; +} SampleModelViewerInputActions; + +typedef struct +{ + SampleVector3 RotationDelta; + SampleVector2 RotationTouch; + SampleVector3 CurrentRotationSpeed; + float PreviousTouchDistance; + float PreviousTouchAngle; + float Zoom; + float Action; +} SampleModelViewerInputState; + +typedef struct +{ + SampleModelViewerInputActions InputActions; + SampleInputActionBindingSpan InputActionBindings; + SampleModelViewerInputState State; +} SampleModelViewerInputs; + +void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) +{ + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyD, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyW, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyS, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyQ, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyE, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyZ, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyX, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Touch); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputs->InputActions.Action); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseLeftButton, 0, SampleInputActionBindingType_Released, &inputs->InputActions.TouchReleased); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseRightButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateSide); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseWheelPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseWheelNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseMiddleButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Touch); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchXAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchPositionX); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchYAbsolutePosition, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchPositionY); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 1, SampleInputActionBindingType_Value, &inputs->InputActions.Touch2); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchXAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputs->InputActions.Touch2PositionX); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchYAbsolutePosition, 1, SampleInputActionBindingType_Value, &inputs->InputActions.Touch2PositionY); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Released, &inputs->InputActions.TouchReleased); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputs->InputActions.Action); +} + +void SampleModelViewerInputsResetTouchParameters(SampleModelViewerInputState* state) +{ + state->RotationTouch = V2Zero; + state->PreviousTouchDistance = 0.0f; + state->PreviousTouchAngle = 0.0f; +} + +void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewerInputs* inputs, float deltaTimeInSeconds) +{ + SampleModelViewerInputState* state = &inputs->State; + SampleModelViewerInputActions* inputActions = &inputs->InputActions; + state->RotationDelta = V3Zero; + + SampleUpdateInputActions(&inputs->InputActionBindings, inputStream); + + if (inputActions->Touch) + { + if (inputActions->Touch2) + { + SampleVector2 touchPosition = (SampleVector2) { inputActions->TouchPositionX, inputActions->TouchPositionY }; + SampleVector2 touchPosition2 = (SampleVector2) { inputActions->Touch2PositionX, inputActions->Touch2PositionY }; + + SampleVector2 diffVector = SampleSubstractV2(touchPosition, touchPosition2); + float distance = SampleMagnitudeV2(diffVector); + float angle = atan2(diffVector.X, diffVector.Y); + + if (state->PreviousTouchDistance != 0.0f) + { + state->Zoom += (distance - state->PreviousTouchDistance) * SAMPLE_MODELVIEWER_ROTATION_ZOOM_MULTITOUCH_SPEED * deltaTimeInSeconds; + } + + if (state->PreviousTouchAngle != 0.0f) + { + state->RotationDelta.Z = -SampleNormalizeAngle(angle - state->PreviousTouchAngle) * SAMPLE_MODELVIEWER_ROTATION_MULTITOUCH_SPEED * deltaTimeInSeconds; + } + + state->PreviousTouchDistance = distance; + state->PreviousTouchAngle = angle; + } + else + { + SampleModelViewerInputsResetTouchParameters(state); + + state->RotationDelta.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + state->RotationDelta.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + } + else if (inputActions->TouchRotateSide) + { + SampleModelViewerInputsResetTouchParameters(state); + state->RotationDelta.Z = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + else if (inputActions->TouchReleased && !inputActions->Touch2) + { + SampleModelViewerInputsResetTouchParameters(state); + + state->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + state->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } + else + { + SampleVector3 direction = SampleNormalizeV3((SampleVector3) + { + .X = (inputActions->RotateUp - inputActions->RotateDown), + .Y = (inputActions->RotateLeft - inputActions->RotateRight), + .Z = (inputActions->RotateSideLeft - inputActions->RotateSideRight) + }); + + if (SampleMagnitudeSquaredV3(direction)) + { + SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, SAMPLE_MODELVIEWER_ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(state->CurrentRotationSpeed), SAMPLE_MODELVIEWER_ROTATION_FRICTION)); + + SampleModelViewerInputsResetTouchParameters(state); + state->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(state->CurrentRotationSpeed, deltaTimeInSeconds)); + state->CurrentRotationSpeed = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), state->CurrentRotationSpeed); + } + } + + if (SampleMagnitudeSquaredV2(state->RotationTouch) > 0) + { + if (SampleMagnitudeV2(state->RotationTouch) > SAMPLE_MODELVIEWER_ROTATION_TOUCH_MAX_DELTA) + { + state->RotationTouch = SampleMulScalarV2(SampleNormalizeV2(state->RotationTouch), SAMPLE_MODELVIEWER_ROTATION_TOUCH_MAX_DELTA); + } + + state->RotationDelta = SampleAddV3(state->RotationDelta, (SampleVector3){ state->RotationTouch.X, state->RotationTouch.Y, 0.0f }); + + SampleVector2 inverse = SampleMulScalarV2(SampleNormalizeV2(SampleInverseV2(state->RotationTouch)), SAMPLE_MODELVIEWER_ROTATION_TOUCH_DECREASE_SPEED); + state->RotationTouch = SampleAddV2(state->RotationTouch, inverse); + + if (SampleMagnitudeV2(state->RotationTouch) < 0.001f) + { + SampleModelViewerInputsResetTouchParameters(state); + } + } + + state->Zoom += (inputActions->ZoomIn - inputActions->ZoomOut) * SAMPLE_MODELVIEWER_ROTATION_ZOOM_SPEED * deltaTimeInSeconds; + state->Action = inputActions->Action; +} diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index c4a54555..d8a56a49 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -446,3 +446,34 @@ SampleFrameMeasurement SampleEndFrameMeasurement(void) .HasNewData = newData }; } + + +// ----------------------------------------------------------------------------- +// Settings Functions +// ----------------------------------------------------------------------------- + +typedef struct +{ + bool PreferVulkan; + bool PreferFullScreen; +} SampleAppSettings; + +SampleAppSettings SampleParseAppSettings(int argc, const char* argv[]) +{ + SampleAppSettings appSettings = {}; + + for (int32_t i = 1; i < argc; i++) + { + if (strcmp(argv[i], "--vulkan") == 0) + { + appSettings.PreferVulkan = true; + } + + if (strcmp(argv[i], "--fullscreen") == 0) + { + appSettings.PreferFullScreen = true; + } + } + + return appSettings; +} diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index 31acbf98..051d0c1c 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -30,6 +30,7 @@ typedef struct float PreviousTouchDistance; float PreviousTouchAngle; float Zoom; + bool IsCursorDisplayed; } GameState; // TODO: Group common variables into separate structs @@ -124,7 +125,6 @@ void InitSample(void* payload) ElemFreeShaderLibrary(shaderLibrary); applicationPayload->ShaderParameters.Transform = SampleCreateIdentityMatrix(); - applicationPayload->InputActions.ShowCursor = true; applicationPayload->GameState.Zoom = 1.0f; applicationPayload->GameState.RotationDelta = 0.0f; @@ -266,16 +266,20 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemExitApplication(0); } - if (inputActions->ShowCursor) - { - ElemShowWindowCursor(applicationPayload->Window); - } - else + GameState* gameState = &applicationPayload->GameState; + + if (inputActions->SwitchShowCursor) { - ElemHideWindowCursor(applicationPayload->Window); + if (!gameState->IsCursorDisplayed) + { + ElemShowWindowCursor(applicationPayload->Window); + } + else + { + ElemHideWindowCursor(applicationPayload->Window); + } } - GameState* gameState = &applicationPayload->GameState; UpdateGameState(gameState, inputActions, updateParameters->DeltaTimeInSeconds); SampleMatrix3x3 transformMatrix = SampleMulMatrix3x3(SampleCreateTranslationMatrix(gameState->TranslationDelta.X, gameState->TranslationDelta.Y), SampleCreateRotationMatrix(gameState->RotationDelta)); diff --git a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl index 819a0583..27f397b6 100644 --- a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl @@ -7,7 +7,7 @@ struct ShaderParameters float4 RotationQuaternion; float Zoom; float AspectRatio; - uint32_t TriangleColor; + uint32_t ShowMeshlets; uint32_t MeshletCount; }; @@ -139,7 +139,7 @@ uint hash(uint a) [shader("pixel")] float4 PixelMain(const VertexOutput input) : SV_Target0 { - if (parameters.TriangleColor == 0) + if (parameters.ShowMeshlets == 0) { return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index f6381456..cd3aa918 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -1,19 +1,10 @@ -#include #include "Elemental.h" #include "SampleUtils.h" #include "SampleMath.h" -#include "SampleInputs.h" +#include "SampleApplicationInputs.h" +#include "SampleModelViewerInputs.h" #include "SampleMesh.h" -#define ROTATION_TOUCH_DECREASE_SPEED 0.001f -#define ROTATION_TOUCH_SPEED 4.0f -#define ROTATION_TOUCH_MAX_DELTA 0.3f -#define ROTATION_MULTITOUCH_SPEED 200.0f -#define ROTATION_ACCELERATION 500.0f -#define ROTATION_FRICTION 60.0f -#define ZOOM_MULTITOUCH_SPEED 1000.0f -#define ZOOM_SPEED 5.0f - typedef struct { uint32_t VertexBuffer; @@ -23,21 +14,10 @@ typedef struct SampleVector4 RotationQuaternion; float Zoom; float AspectRatio; - uint32_t TriangeColor; + uint32_t ShowMeshlets; uint32_t MeshletCount; } ShaderParameters; -// TODO: Extract from the gamestate the inputState -typedef struct -{ - SampleVector3 RotationDelta; - SampleVector2 RotationTouch; - SampleVector3 CurrentRotationSpeed; - float PreviousTouchDistance; - float PreviousTouchAngle; - float Zoom; -} GameState; - typedef struct { uint32_t MeshletCount; @@ -51,12 +31,6 @@ typedef struct ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; } MeshData; -typedef struct -{ - bool PreferVulkan; - bool PreferFullScreen; -} SampleAppSettings; - // TODO: Group common variables into separate structs typedef struct { @@ -72,9 +46,8 @@ typedef struct ElemGraphicsResource DepthBuffer; ElemPipelineState GraphicsPipeline; ShaderParameters ShaderParameters; - SampleModelViewerInputActions InputActions; - SampleInputActionBindingSpan InputActionBindings; - GameState GameState; + SampleApplicationInputs ApplicationInputs; + SampleModelViewerInputs ModelViewerInputs; MeshData TestMeshData; } ApplicationPayload; @@ -141,12 +114,33 @@ void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicat applicationPayload->ShaderParameters.MeshletCount = meshData->MeshletCount; } +void FreeMesh(MeshData* meshData) +{ + ElemFreeGraphicsResourceDescriptor(meshData->VertexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->VertexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletVertexIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletVertexIndexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletTriangleIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletTriangleIndexBuffer, NULL); +} + void InitSample(void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; applicationPayload->Window = ElemCreateWindow(&(ElemWindowOptions) { .WindowState = applicationPayload->AppSettings.PreferFullScreen ? ElemWindowState_FullScreen : ElemWindowState_Normal }); ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); + + // TODO: Debug why the AMD integrated GPU is not create at all + ElemGraphicsDeviceInfoSpan devices = ElemGetAvailableGraphicsDevices(); + + for (uint32_t i = 0; i < devices.Length; i++) + { + printf("Device: %s\n", devices.Items[i].DeviceName); + } + applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); @@ -182,10 +176,16 @@ void InitSample(void* payload) ElemFreeShaderLibrary(shaderLibrary); applicationPayload->ShaderParameters.RotationQuaternion = (SampleVector4){ .X = 0, .Y = 0, .Z = 0, .W = 1 }; - applicationPayload->InputActions.ShowCursor = true; - SampleRegisterModelViewerInputBindings(&applicationPayload->InputActionBindings, &applicationPayload->InputActions); + SampleApplicationInputsInit(&applicationPayload->ApplicationInputs); + SampleModelViewerInputsInit(&applicationPayload->ModelViewerInputs); + if (applicationPayload->AppSettings.PreferFullScreen) + { + ElemHideWindowCursor(applicationPayload->Window); + applicationPayload->ApplicationInputs.State.IsCursorDisplayed = false; + } + SampleStartFrameMeasurement(); } @@ -195,20 +195,12 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); + FreeMesh(&applicationPayload->TestMeshData); + ElemFreePipelineState(applicationPayload->GraphicsPipeline); ElemFreeSwapChain(applicationPayload->SwapChain); ElemFreeCommandQueue(applicationPayload->CommandQueue); - // Free Mesh - ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.VertexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->TestMeshData.VertexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletVertexIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletVertexIndexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(applicationPayload->TestMeshData.MeshletTriangleIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->TestMeshData.MeshletTriangleIndexBuffer, NULL); - ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); @@ -216,102 +208,6 @@ void FreeSample(void* payload) ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); } -void ResetTouchParameters(GameState* gameState) -{ - gameState->RotationTouch = V2Zero; - gameState->PreviousTouchDistance = 0.0f; - gameState->PreviousTouchAngle = 0.0f; -} - -// TODO: Extract model view gamestate update -void UpdateGameState(GameState* gameState, SampleModelViewerInputActions* inputActions, float deltaTimeInSeconds) -{ - gameState->RotationDelta = V3Zero; - - if (inputActions->Touch) - { - if (inputActions->Touch2) - { - SampleVector2 touchPosition = (SampleVector2) { inputActions->TouchPositionX, inputActions->TouchPositionY }; - SampleVector2 touchPosition2 = (SampleVector2) { inputActions->Touch2PositionX, inputActions->Touch2PositionY }; - - SampleVector2 diffVector = SampleSubstractV2(touchPosition, touchPosition2); - float distance = SampleMagnitudeV2(diffVector); - float angle = atan2(diffVector.X, diffVector.Y); - - if (gameState->PreviousTouchDistance != 0.0f) - { - gameState->Zoom += (distance - gameState->PreviousTouchDistance) * ZOOM_MULTITOUCH_SPEED * deltaTimeInSeconds; - } - - if (gameState->PreviousTouchAngle != 0.0f) - { - gameState->RotationDelta.Z = -SampleNormalizeAngle(angle - gameState->PreviousTouchAngle) * ROTATION_MULTITOUCH_SPEED * deltaTimeInSeconds; - } - - gameState->PreviousTouchDistance = distance; - gameState->PreviousTouchAngle = angle; - } - else - { - ResetTouchParameters(gameState); - - gameState->RotationDelta.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - gameState->RotationDelta.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - } - } - else if (inputActions->TouchRotateSide) - { - ResetTouchParameters(gameState); - gameState->RotationDelta.Z = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - } - else if (inputActions->TouchReleased && !inputActions->Touch2) - { - ResetTouchParameters(gameState); - - gameState->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - gameState->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * ROTATION_TOUCH_SPEED * deltaTimeInSeconds; - } - else - { - SampleVector3 direction = SampleNormalizeV3((SampleVector3) - { - .X = (inputActions->RotateUp - inputActions->RotateDown), - .Y = (inputActions->RotateLeft - inputActions->RotateRight), - .Z = (inputActions->RotateSideLeft - inputActions->RotateSideRight) - }); - - if (SampleMagnitudeSquaredV3(direction)) - { - SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(gameState->CurrentRotationSpeed), ROTATION_FRICTION)); - - ResetTouchParameters(gameState); - gameState->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(gameState->CurrentRotationSpeed, deltaTimeInSeconds)); - gameState->CurrentRotationSpeed = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), gameState->CurrentRotationSpeed); - } - } - - if (SampleMagnitudeSquaredV2(gameState->RotationTouch) > 0) - { - if (SampleMagnitudeV2(gameState->RotationTouch) > ROTATION_TOUCH_MAX_DELTA) - { - gameState->RotationTouch = SampleMulScalarV2(SampleNormalizeV2(gameState->RotationTouch), ROTATION_TOUCH_MAX_DELTA); - } - - gameState->RotationDelta = SampleAddV3(gameState->RotationDelta, (SampleVector3){ gameState->RotationTouch.X, gameState->RotationTouch.Y, 0.0f }); - - SampleVector2 inverse = SampleMulScalarV2(SampleNormalizeV2(SampleInverseV2(gameState->RotationTouch)), ROTATION_TOUCH_DECREASE_SPEED); - gameState->RotationTouch = SampleAddV2(gameState->RotationTouch, inverse); - - if (SampleMagnitudeV2(gameState->RotationTouch) < 0.001f) - { - ResetTouchParameters(gameState); - } - } - - gameState->Zoom += (inputActions->ZoomIn - inputActions->ZoomOut) * ZOOM_SPEED * deltaTimeInSeconds; -} - void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; @@ -321,41 +217,42 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void CreateDepthBuffer(applicationPayload, updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height); } - SampleModelViewerInputActions* inputActions = &applicationPayload->InputActions; ElemInputStream inputStream = ElemGetInputStream(); - // TODO: PAss the input stream as a parameter - SampleUpdateInputActions(&applicationPayload->InputActionBindings, inputStream); - if (inputActions->ExitApp) + SampleApplicationInputsUpdate(inputStream, &applicationPayload->ApplicationInputs, updateParameters->DeltaTimeInSeconds); + SampleModelViewerInputsUpdate(inputStream, &applicationPayload->ModelViewerInputs, updateParameters->DeltaTimeInSeconds); + + if (applicationPayload->ApplicationInputs.State.ExitApplication) { ElemExitApplication(0); } - if (inputActions->ShowCursor) + if (applicationPayload->ApplicationInputs.State.ShowCursor) { + printf("Show cursor\n"); ElemShowWindowCursor(applicationPayload->Window); } - else + else if (applicationPayload->ApplicationInputs.State.HideCursor) { - ElemHideWindowCursor(applicationPayload->Window); - } + printf("Hide cursor\n"); + ElemHideWindowCursor(applicationPayload->Window); + } - GameState* gameState = &applicationPayload->GameState; - UpdateGameState(gameState, inputActions, updateParameters->DeltaTimeInSeconds); + SampleModelViewerInputState* modelViewerState = &applicationPayload->ModelViewerInputs.State; - if (SampleMagnitudeSquaredV3(gameState->RotationDelta)) + if (SampleMagnitudeSquaredV3(modelViewerState->RotationDelta)) { - SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, gameState->RotationDelta.X), - SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, gameState->RotationDelta.Z), - SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, gameState->RotationDelta.Y))); + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, modelViewerState->RotationDelta.X), + SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, modelViewerState->RotationDelta.Z), + SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, modelViewerState->RotationDelta.Y))); applicationPayload->ShaderParameters.RotationQuaternion = SampleMulQuat(rotationQuaternion, applicationPayload->ShaderParameters.RotationQuaternion); } applicationPayload->ShaderParameters.AspectRatio = updateParameters->SwapChainInfo.AspectRatio; float maxZoom = (applicationPayload->ShaderParameters.AspectRatio >= 0.75 ? 1.5f : 3.5f); - applicationPayload->ShaderParameters.Zoom = fminf(maxZoom, gameState->Zoom); - applicationPayload->ShaderParameters.TriangeColor = inputActions->TriangleColor; + applicationPayload->ShaderParameters.Zoom = fminf(maxZoom, modelViewerState->Zoom); + applicationPayload->ShaderParameters.ShowMeshlets = modelViewerState->Action; ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); @@ -397,25 +294,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void int main(int argc, const char* argv[]) { - // TODO: Extract arguments parsing code to sample - SampleAppSettings appSettings = {}; - - for (int32_t i = 1; i < argc; i++) - { - if (strcmp(argv[i], "--vulkan") == 0) - { - appSettings.PreferVulkan = true; - } - - if (strcmp(argv[i], "--fullscreen") == 0) - { - appSettings.PreferFullScreen = true; - } - } - ApplicationPayload payload = { - .AppSettings = appSettings + .AppSettings = SampleParseAppSettings(argc, argv) }; ElemConfigureLogHandler(ElemConsoleLogHandler); diff --git a/src/Elemental/Common/Inputs/HidDevices.cpp b/src/Elemental/Common/Inputs/HidDevices.cpp index c202658f..ef0193fc 100644 --- a/src/Elemental/Common/Inputs/HidDevices.cpp +++ b/src/Elemental/Common/Inputs/HidDevices.cpp @@ -1,6 +1,7 @@ #include "HidDevices.h" #include "HidUtils.h" #include "HidSwitchGamepad.h" +#include "HidDualSenseGamepad.h" #include "Inputs.h" #include "SystemFunctions.h" @@ -318,6 +319,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe }); } +/* void ProcessDualSenseGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) { auto reportId = hidReport[0]; @@ -458,15 +460,15 @@ void ProcessDualSenseGamepadReport(ElemWindow window, ElemInputDevice inputDevic .InputType = ElemInputType_Digital, .Value = (inputReport->Buttons & 0x20) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds - });*/ -} + }); +}*/ HidGamepadHandler HidGamepadHandlers[] = { { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWirelessOldDriver, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneUsb, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWireless, .ProcessDataHandler = ProcessXboxOneWirelessGamepadReport }, - { .Vendor = HidGamepadVendor_Sony, .Product = HidGamepadProduct_DualSense, .ProcessDataHandler = ProcessDualSenseGamepadReport }, + { .Vendor = HidGamepadVendor_Sony, .Product = HidGamepadProduct_DualSense, .ProcessDataHandler = ProcessHidDualSenseGamepadInputReport }, { .Vendor = HidGamepadVendor_Nintendo, .Product = HidGamepadProduct_SwitchPro, .ProcessDataHandler = ProcessHidSwitchGamepadInputReport } }; diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.h b/src/Elemental/Common/Inputs/HidDualSenseGamepad.h new file mode 100644 index 00000000..4604a1fe --- /dev/null +++ b/src/Elemental/Common/Inputs/HidDualSenseGamepad.h @@ -0,0 +1,309 @@ +#pragma once + +#include "../Elemental.h" +#include "Inputs.h" +#include "HidUtils.h" +#include "SystemFunctions.h" +#include "SystemDataPool.h" + +// Based on the spec: https://controllers.fandom.com/wiki/Sony_DualSense + +#define HID_DUALSENSE_STICK_RANGE 255 +#define HID_DUALSENSE_STICK_DEAD_ZONE 0.1f + +enum HidDualSenseGamepadInputReportType +{ + HidDualSenseGamepadInputReportType_BluetoothStandard = 0x01, + //HidDualSenseGamepadInputReportType_SubCommand = 0x21 +}; + +enum HidDualSenseGamepadOutputReportType +{ + //HidDualSenseGamepadOutputReportType_SubCommand = 0x01 +}; + +/* +enum HidDualSenseGamepadSubCommandType : uint8_t +{ + HidDualSenseGamepadSubCommandType_DeviceInfo = 0x02 +}; + +enum HidDualSenseGamepadControllerType : uint8_t +{ + HidDualSenseGamepadControllerType_LeftJoyCon = 0x01, + HidDualSenseGamepadControllerType_RightJoyCon = 0x02, + HidDualSenseGamepadControllerType_ProController = 0x03, +};*/ + +enum HidDualSenseGamepadButton : uint16_t +{ + HidDualSenseGamepadButton_Square = 1 << 4, + HidDualSenseGamepadButton_Cross = 1 << 5, + HidDualSenseGamepadButton_Circle = 1 << 6, + HidDualSenseGamepadButton_Triangle = 1 << 7, + HidDualSenseGamepadButton_L1 = 1 << 8, + HidDualSenseGamepadButton_R1 = 1 << 9, + HidDualSenseGamepadButton_L2 = 1 << 10, + HidDualSenseGamepadButton_R2 = 1 << 11, + HidDualSenseGamepadButton_Share = 1 << 12, + HidDualSenseGamepadButton_Options = 1 << 13, + HidDualSenseGamepadButton_L3 = 1 << 14, + HidDualSenseGamepadButton_R3 = 1 << 15 +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadInputReportBluetoothStandard +{ + uint8_t LeftStickX; + uint8_t LeftStickY; + uint8_t RightStickX; + uint8_t RightStickY; + uint16_t Buttons; + uint8_t SystemButtons; + uint8_t LeftTrigger; + uint8_t RightTrigger; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadInputReportExtended +{ + uint8_t Timer; + uint8_t BatteryConnection; + uint8_t Buttons[3]; + uint8_t LeftStick[3]; + uint8_t RightStick[3]; +}; + +struct HidDualSenseGamepadData +{ + float PreviousLeftStickX; + float PreviousLeftStickY; + uint16_t PreviousButtons; +}; + +/* +struct __attribute__((__packed__)) HidDualSenseGamepadInputReportDeviceInfo +{ + uint16_t FirmwareVersion; + HidDualSenseGamepadControllerType ControllerType; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadOutputReportSubCommand +{ + uint8_t ReportId; + uint8_t GlobalPacketNumber; + // TODO: Rumble Data + uint8_t RumbleData[8]; + HidDualSenseGamepadSubCommandType SubCommandId; + uint8_t Data[53]; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportSetupMemoryRead +{ + uint8_t ReportId; + uint32_t Address; + uint16_t Size; + uint8_t Checksum; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportMemoryRead +{ + uint8_t ReportId; + uint8_t Checksum; + uint8_t Data[1024]; +};*/ + +static SystemDataPool hidDualSenseGamepadDataPool; + +inline void InitHidDualSenseGamepadMemory() +{ + if (hidDualSenseGamepadDataPool.Storage == nullptr) + { + hidDualSenseGamepadDataPool = SystemCreateDataPool(InputsMemoryArena, MAX_INPUT_DEVICES); + } +} + +inline HidDualSenseGamepadData* GetHidDualSenseGamepadData(ElemHandle hidDevice) +{ + return SystemGetDataPoolItem(hidDualSenseGamepadDataPool, hidDevice); +} + +/* +inline bool SendHidDualSenseGamepadSubCommand(ElemInputDevice inputDevice, HidDualSenseGamepadSubCommandType subCommandType, ReadOnlySpan data) +{ + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + HidDualSenseGamepadOutputReportSubCommand outputReport = + { + .ReportId = HidDualSenseGamepadOutputReportType_SubCommand, + .GlobalPacketNumber = hidData->CurrentOutputPacketNumber, + .SubCommandId = subCommandType + }; + + hidData->CurrentOutputPacketNumber = (hidData->CurrentOutputPacketNumber + 1) % 255; + + return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); +}*/ + +inline void ProcessHidDualSenseGamepadStick(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float stickData, float previousStickData, ElemInputId negativeInputId, ElemInputId positiveInputId) +{ + if (stickData != previousStickData) + { + if (stickData <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = negativeInputId, + .InputType = ElemInputType_Analog, + .Value = -stickData, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (stickData >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = positiveInputId, + .InputType = ElemInputType_Analog, + .Value = stickData, + .ElapsedSeconds = elapsedSeconds + }); + } + } +} + +inline void ProcessHidDualSenseGamepadButton(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, uint16_t currentButtons, uint16_t previousButtons, HidDualSenseGamepadButton buttonType, ElemInputId inputId) +{ + if ((currentButtons & buttonType) != (previousButtons & buttonType)) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = inputId, + .InputType = ElemInputType_Digital, + .Value = (currentButtons & buttonType) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + } +} + +inline void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReportBluetoothStandard* inputReport, double elapsedSeconds) +{ + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "DualSenseGamePad Bluetooth Standard Report %f", elapsedSeconds); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + if (inputDeviceData->HidDeviceData != ELEM_HANDLE_NULL) + { + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + auto leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidDualSenseGamepadStick(window, inputDevice, elapsedSeconds, leftStickX, hidData->PreviousLeftStickX, ElemInputId_GamepadLeftStickXNegative, ElemInputId_GamepadLeftStickXPositive); + + auto leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidDualSenseGamepadStick(window, inputDevice, elapsedSeconds, leftStickY, hidData->PreviousLeftStickY, ElemInputId_GamepadLeftStickYNegative, ElemInputId_GamepadLeftStickYPositive); + + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Cross, ElemInputID_GamepadButtonA); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Circle, ElemInputID_GamepadButtonB); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Square, ElemInputID_GamepadButtonX); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Triangle, ElemInputID_GamepadButtonY); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L1, ElemInputID_GamepadLeftShoulder); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R1, ElemInputID_GamepadRightShoulder); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Share, ElemInputID_GamepadButtonShare); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Options, ElemInputID_GamepadButtonOptions); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L3, ElemInputId_GamepadLeftStickButton); + ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R3, ElemInputId_GamepadRightStickButton); + + // TODO: TESTING ONLY + if (inputReport->Buttons & 0x01) + { + //SendHidDualSenseGamepadSubCommand(inputDevice, HidDualSenseGamepadSubCommandType_DeviceInfo, ReadOnlySpan()); + } + + hidData->PreviousLeftStickX = leftStickX; + hidData->PreviousLeftStickY = leftStickY; + hidData->PreviousButtons = inputReport->Buttons; + } +} + +inline void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + // TODO: We need to differentiate USB / Bluetooth? How can we do that? + + InitHidDualSenseGamepadMemory(); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + // TODO: This part is to get the calibration data + if (inputDeviceData->HidDeviceData == ELEM_HANDLE_NULL) + { + /* + // Read Config + HidDualSenseGamepadFeatureReportSetupMemoryRead featureReport = + { + .ReportId = 0x71, + .Address = 0xF8000000 + 0x6000, + .Size = 0xA000, + .Checksum = 0 + }; + + auto pointer = (uint8_t*)&featureReport; + auto sumBytes = 0u; + + for (uint32_t i = 0; i < sizeof(featureReport); i++) + { + sumBytes += pointer[i]; + } + + featureReport.Checksum = 0x100 - sumBytes; + + auto result = PlatformHidSendFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&featureReport, sizeof(featureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Send Feature Result: %d", result); + + HidDualSenseGamepadFeatureReportMemoryRead readFeatureReport = + { + .ReportId = 0x72, + .Checksum = 0 + }; + + result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&readFeatureReport, sizeof(readFeatureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result);*/ + + inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidDualSenseGamepadDataPool, {}); + } + + auto reportId = hidReport[0]; + + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "DualSenseGamePad %d", reportId); + + // TODO: Use the the advanced report + if (reportId == HidDualSenseGamepadInputReportType_BluetoothStandard) + { + auto inputReport = (HidDualSenseGamepadInputReportBluetoothStandard*)(++hidReport.Pointer); + ProcessHidDualSenseGamepadInputReportBluetoothStandard(window, inputDevice, inputReport, elapsedSeconds); + } + /* + else if (reportId == HidDualSenseGamepadInputReportType_SubCommand) + { + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + + auto inputReport = (HidDualSenseGamepadInputReportExtended*)(++hidReport.Pointer); + + uint8_t *data = inputReport->LeftStick; + uint16_t stick_horizontal = data[0] | ((data[1] & 0xF) << 8); + uint16_t stick_vertical = (data[1] >> 4) | (data[2] << 4); + + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Report: Timer=%d, X=%d, Y=%d", inputReport->Timer, stick_horizontal, stick_vertical); + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); + }*/ +} diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 3bede491..a8c89ab0 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -277,6 +277,9 @@ ElemAPI void ElemSetWindowState(ElemWindow window, ElemWindowState windowState); // TODO: Function to get monitor infos +// TODO: We need to be able to detect if the window is in focus mode. When a game loose focus, +// it should be able to disable mouse tracking (for camera etc.) + // TODO: Comments // TODO: Make sure the coordinates are consistent accross all platforms ElemAPI void ElemShowWindowCursor(ElemWindow window); @@ -378,6 +381,7 @@ typedef enum typedef enum { ElemGraphicsHeapType_Gpu = 0, + // TODO: Get rid of this? ElemGraphicsHeapType_GpuUpload = 1, ElemGraphicsHeapType_Readback = 2 } ElemGraphicsHeapType; @@ -1019,6 +1023,7 @@ ElemAPI ElemFence ElemExecuteCommandList(ElemCommandQueue commandQueue, ElemComm */ ElemAPI ElemFence ElemExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandListSpan commandLists, const ElemExecuteCommandListOptions* options); +// TODO: Still in draft! ElemAPI ElemIOCommandQueue ElemCreateIOCommandQueue(const ElemIOCommandQueueOptions* options); ElemAPI void ElemFreeIOCommandQueue(ElemIOCommandQueue ioQueue); ElemAPI void ElemEnqueueIOCommand(ElemIOCommandQueue ioCommandQueue, const ElemIOCommandParameters* parameters); @@ -1334,7 +1339,7 @@ typedef enum ElemInputID_GamepadButtonB = 119, ElemInputID_GamepadButtonX = 120, ElemInputID_GamepadButtonY = 121, - ElemInputID_GamepadButtonMenu = 122, + ElemInputID_GamepadButtonShare = 122, ElemInputID_GamepadButtonOptions = 123, ElemInputID_GamepadButtonHome = 124, ElemInputID_GamepadLeftShoulder = 125, diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index c7efca69..2c276856 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -170,6 +170,8 @@ bool DirectX12CheckGraphicsDeviceCompatibility(ComPtr graphicsDe shaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_8; AssertIfFailed(graphicsDevice->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel))); + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Test"); + // TODO: Update checks if (deviceOptions.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_2 && deviceOptions.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3 && diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index f0ee6151..00de0c77 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -62,6 +62,8 @@ void ResizeDirectX12SwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t { return; } + + // BUG: When switching to a screen with a different refresh rate! SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Resize Swapchain to %dx%d.", width, height); SystemAssert(swapChain != ELEM_HANDLE_NULL); @@ -334,6 +336,8 @@ ElemSwapChain DirectX12CreateSwapChain(ElemCommandQueue commandQueue, ElemWindow .Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT #endif }; + + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Create swapchain with size: %dx%d@%f. (%dHz)", windowRenderSize.Width, windowRenderSize.Height, windowRenderSize.UIScale, targetFPS); ComPtr swapChain; AssertIfFailedReturnNullHandle(DxgiFactory->CreateSwapChainForHwnd(commandQueueData->DeviceObject.Get(), windowData->WindowHandle, &swapChainDesc, nullptr, nullptr, (IDXGISwapChain1**)swapChain.GetAddressOf())); diff --git a/src/Elemental/Microsoft/Win32Window.cpp b/src/Elemental/Microsoft/Win32Window.cpp index 10a43b07..4ec840bf 100644 --- a/src/Elemental/Microsoft/Win32Window.cpp +++ b/src/Elemental/Microsoft/Win32Window.cpp @@ -343,6 +343,7 @@ ElemAPI void ElemFreeWindow(ElemWindow window) SystemRemoveDataPoolItem(windowDataPool, window); } +// BUG: We have a bug when we switch to the TV with the refresh rate ElemAPI ElemWindowSize ElemGetWindowRenderSize(ElemWindow window) { SystemAssert(window != ELEM_HANDLE_NULL); From d69d3047469ea953195dc1e802a126b017219fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 29 Nov 2024 06:15:39 +0100 Subject: [PATCH 31/93] Update Dualsense code --- samples/Common/SampleApplicationInputs.h | 2 +- samples/Common/SampleInputs.h | 12 +- samples/Common/SampleModelViewerInputs.h | 10 +- samples/Elemental/03-HelloInputs/main.c | 10 +- .../Graphics/Vulkan/VulkanCommandList.cpp | 1 - .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 4 +- .../Graphics/Vulkan/VulkanSwapChain.cpp | 2 +- src/Elemental/Common/Inputs/HidDevices.cpp | 168 ++---------------- .../Common/Inputs/HidDualSenseGamepad.h | 68 +++++-- src/Elemental/Elemental.h | 22 +-- .../Microsoft/Graphics/DirectX12Shader.cpp | 7 +- .../Microsoft/Graphics/DirectX12SwapChain.cpp | 2 +- 12 files changed, 101 insertions(+), 207 deletions(-) diff --git a/samples/Common/SampleApplicationInputs.h b/samples/Common/SampleApplicationInputs.h index 45b72438..02b096eb 100644 --- a/samples/Common/SampleApplicationInputs.h +++ b/samples/Common/SampleApplicationInputs.h @@ -29,7 +29,7 @@ void SampleApplicationInputsInit(SampleApplicationInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_Released, &inputs->InputActions.SwitchShowCursor); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); inputs->State.IsCursorDisplayed = true; } diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index 20498a07..83ac7e07 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -159,12 +159,12 @@ void SampleRegisterStandardInputBindings(SampleInputActionBindingSpan* inputActi SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputActions->TranslateUp); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputActions->TranslateDown); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftStickButton, 0, SampleInputActionBindingType_Value, &inputActions->Action1); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->Action1); - SampleRegisterInputActionBinding(inputActionBindings, ElemInputID_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideLeft); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputActions->RotateSideRight); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomOut); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputActions->ZoomIn); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputActions->Action1); + SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_GamepadButtonB, 0, SampleInputActionBindingType_Released, &inputActions->ExitApp); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputActions->Touch); SampleRegisterInputActionBinding(inputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputActions->TouchTranslateLeft); diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleModelViewerInputs.h index df5e3003..47fa2006 100644 --- a/samples/Common/SampleModelViewerInputs.h +++ b/samples/Common/SampleModelViewerInputs.h @@ -88,11 +88,11 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateDown); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputID_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Touch); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_TouchXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateLeft); diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 72d5fb2c..651d8b12 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -142,11 +142,11 @@ void RegisterInputBindings(ApplicationPayload* applicationPayload) RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateUp); RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateDown); RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadLeftStickButton, 0, InputActionBindingType_Value, &applicationPayload->InputActions.TriangleColor); - RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputID_GamepadLeftTrigger, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateSideLeft); - RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputID_GamepadRightTrigger, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateSideRight); - RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputID_GamepadLeftShoulder, 0, InputActionBindingType_Value, &applicationPayload->InputActions.ZoomOut); - RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputID_GamepadRightShoulder, 0, InputActionBindingType_Value, &applicationPayload->InputActions.ZoomIn); - RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputID_GamepadButtonA, 0, InputActionBindingType_Value, &applicationPayload->InputActions.TriangleColor); + RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadLeftTrigger, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateSideLeft); + RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadRightTrigger, 0, InputActionBindingType_Value, &applicationPayload->InputActions.RotateSideRight); + RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadLeftShoulder, 0, InputActionBindingType_Value, &applicationPayload->InputActions.ZoomOut); + RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadRightShoulder, 0, InputActionBindingType_Value, &applicationPayload->InputActions.ZoomIn); + RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_GamepadButtonA, 0, InputActionBindingType_Value, &applicationPayload->InputActions.TriangleColor); RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_Touch, 0, InputActionBindingType_Value, &applicationPayload->InputActions.Touch); RegisterInputActionBinding(&applicationPayload->InputActionBindings, ElemInputId_TouchXNegative, 0, InputActionBindingType_Value, &applicationPayload->InputActions.TouchRotateLeft); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index 4056f6c0..57563e3a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -462,7 +462,6 @@ bool VulkanIsFenceCompleted(ElemFence fence) SystemAssert(fence.CommandQueue != ELEM_HANDLE_NULL); auto commandQueueToWaitData = GetVulkanCommandQueueData(fence.CommandQueue); - auto commandQueueToWaitDataFull = GetVulkanCommandQueueDataFull(fence.CommandQueue); if (!commandQueueToWaitData) { diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 4c1af54a..c0c0f4d0 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -341,8 +341,8 @@ void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) VkMutableDescriptorTypeListEXT descriptorSetTypeList = { - .pDescriptorTypes = resourceDescriptorTypes, - .descriptorTypeCount = ARRAYSIZE(resourceDescriptorTypes) + .descriptorTypeCount = ARRAYSIZE(resourceDescriptorTypes), + .pDescriptorTypes = resourceDescriptorTypes }; VkMutableDescriptorTypeCreateInfoEXT mutableDescriptorTypeCreateInfo = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT }; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index ba96aa88..23e53c28 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -392,7 +392,7 @@ ElemSwapChain VulkanCreateSwapChain(ElemCommandQueue commandQueue, ElemWindow wi VkFenceCreateInfo fenceCreateInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; AssertIfFailed(vkCreateFence(graphicsDeviceData->Device, &fenceCreateInfo, 0, &acquireFence )); - uint64_t creationTimestamp; + uint64_t creationTimestamp = 0; // TODO: Put that in a system function //QueryPerformanceCounter(&creationTimestamp); diff --git a/src/Elemental/Common/Inputs/HidDevices.cpp b/src/Elemental/Common/Inputs/HidDevices.cpp index ef0193fc..3cc54530 100644 --- a/src/Elemental/Common/Inputs/HidDevices.cpp +++ b/src/Elemental/Common/Inputs/HidDevices.cpp @@ -129,7 +129,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftTrigger, + .InputId = ElemInputId_GamepadLeftTrigger, .InputType = ElemInputType_Analog, .Value = NormalizeInputValue(xboxReport->LeftTrigger, 1024, 0.1f), .ElapsedSeconds = elapsedSeconds @@ -138,7 +138,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightTrigger, + .InputId = ElemInputId_GamepadRightTrigger, .InputType = ElemInputType_Analog, .Value = NormalizeInputValue(xboxReport->RightTrigger, 1024, 0.1f), .ElapsedSeconds = elapsedSeconds @@ -150,7 +150,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonA, + .InputId = ElemInputId_GamepadButtonA, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -159,7 +159,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonB, + .InputId = ElemInputId_GamepadButtonB, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -168,7 +168,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftShoulder, + .InputId = ElemInputId_GamepadLeftShoulder, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x40) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -177,7 +177,7 @@ void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inpu AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightShoulder, + .InputId = ElemInputId_GamepadRightShoulder, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x80) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -262,7 +262,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftTrigger, + .InputId = ElemInputId_GamepadLeftTrigger, .InputType = ElemInputType_Analog, .Value = NormalizeInputValue(left, 255, 0.1f), .ElapsedSeconds = elapsedSeconds @@ -271,7 +271,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightTrigger, + .InputId = ElemInputId_GamepadRightTrigger, .InputType = ElemInputType_Analog, .Value = NormalizeInputValue(right, 255, 0.1f), .ElapsedSeconds = elapsedSeconds @@ -285,7 +285,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonA, + .InputId = ElemInputId_GamepadButtonA, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -294,7 +294,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonB, + .InputId = ElemInputId_GamepadButtonB, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -303,7 +303,7 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftShoulder, + .InputId = ElemInputId_GamepadLeftShoulder, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x10) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds @@ -312,157 +312,13 @@ void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDe AddInputEvent({ .Window = window, .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightShoulder, + .InputId = ElemInputId_GamepadRightShoulder, .InputType = ElemInputType_Digital, .Value = (xboxReport->Buttons & 0x20) ? 1.0f : 0.0f, .ElapsedSeconds = elapsedSeconds }); } -/* -void ProcessDualSenseGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) -{ - auto reportId = hidReport[0]; - - if (reportId == 1) - { - auto inputReport = (DualSenseSimpleGamepadReport*)(++hidReport.Pointer); - - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Process DualSense %d %d", reportId, inputReport->LeftStickX); - - // TODO: Also here we need to send the event only if the analog value has changed - // TODO: We can refactor this function by taking the sizeof of the field? - float leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, 256, 0.2f); - - if (leftStickX <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - - if (leftStickX >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - } -/* - float leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, 65535, 0.2f); - - if (leftStickY <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - // TODO: check if the value is inversed!! - if (leftStickY >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - - const uint16_t midpoint = 32768; - - uint8_t left, right; - - // Calculate the right trigger value as a difference from midpoint - if (inputReport->Triggers <= midpoint) { - right = static_cast((midpoint - inputReport->Triggers) >> 7); - } else { - right = 0; - } - - // Calculate the left trigger value as a difference from midpoint - if (inputReport->Triggers >= midpoint) { - left = static_cast((inputReport->Triggers - midpoint) >> 7); - } else { - left = 0; - } - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(left, 255, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(right, 255, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - // TODO: For buttons we need to keep track of old report to see if there was a change - // Maybe we can do the same as with delta inputs! - - - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonA, - .InputType = ElemInputType_Digital, - .Value = (inputReport->Buttons & 0x01) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadButtonB, - .InputType = ElemInputType_Digital, - .Value = (inputReport->Buttons & 0x02) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadLeftShoulder, - .InputType = ElemInputType_Digital, - .Value = (inputReport->Buttons & 0x10) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputID_GamepadRightShoulder, - .InputType = ElemInputType_Digital, - .Value = (inputReport->Buttons & 0x20) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); -}*/ - HidGamepadHandler HidGamepadHandlers[] = { { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWirelessOldDriver, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.h b/src/Elemental/Common/Inputs/HidDualSenseGamepad.h index 4604a1fe..6e2163d4 100644 --- a/src/Elemental/Common/Inputs/HidDualSenseGamepad.h +++ b/src/Elemental/Common/Inputs/HidDualSenseGamepad.h @@ -76,6 +76,10 @@ struct HidDualSenseGamepadData { float PreviousLeftStickX; float PreviousLeftStickY; + float PreviousRightStickX; + float PreviousRightStickY; + float PreviousLeftTrigger; + float PreviousRightTrigger; uint16_t PreviousButtons; }; @@ -147,7 +151,8 @@ inline bool SendHidDualSenseGamepadSubCommand(ElemInputDevice inputDevice, HidDu return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); }*/ -inline void ProcessHidDualSenseGamepadStick(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float stickData, float previousStickData, ElemInputId negativeInputId, ElemInputId positiveInputId) +// TODO: Promote those functions to common header +inline void ProcessHidGamepadStick(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float stickData, float previousStickData, ElemInputId negativeInputId, ElemInputId positiveInputId) { if (stickData != previousStickData) { @@ -177,7 +182,22 @@ inline void ProcessHidDualSenseGamepadStick(ElemWindow window, ElemInputDevice i } } -inline void ProcessHidDualSenseGamepadButton(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, uint16_t currentButtons, uint16_t previousButtons, HidDualSenseGamepadButton buttonType, ElemInputId inputId) +inline void ProcessHidGamepadTrigger(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float data, float previousData, ElemInputId inputId) +{ + if (data != previousData) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = inputId, + .InputType = ElemInputType_Analog, + .Value = data, + .ElapsedSeconds = elapsedSeconds + }); + } +} + +inline void ProcessHidGamepadButton(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, uint16_t currentButtons, uint16_t previousButtons, uint16_t buttonType, ElemInputId inputId) { if ((currentButtons & buttonType) != (previousButtons & buttonType)) { @@ -205,21 +225,35 @@ inline void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow wi SystemAssert(hidData); auto leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidDualSenseGamepadStick(window, inputDevice, elapsedSeconds, leftStickX, hidData->PreviousLeftStickX, ElemInputId_GamepadLeftStickXNegative, ElemInputId_GamepadLeftStickXPositive); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickX, hidData->PreviousLeftStickX, ElemInputId_GamepadLeftStickXNegative, ElemInputId_GamepadLeftStickXPositive); auto leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidDualSenseGamepadStick(window, inputDevice, elapsedSeconds, leftStickY, hidData->PreviousLeftStickY, ElemInputId_GamepadLeftStickYNegative, ElemInputId_GamepadLeftStickYPositive); - - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Cross, ElemInputID_GamepadButtonA); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Circle, ElemInputID_GamepadButtonB); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Square, ElemInputID_GamepadButtonX); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Triangle, ElemInputID_GamepadButtonY); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L1, ElemInputID_GamepadLeftShoulder); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R1, ElemInputID_GamepadRightShoulder); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Share, ElemInputID_GamepadButtonShare); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Options, ElemInputID_GamepadButtonOptions); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L3, ElemInputId_GamepadLeftStickButton); - ProcessHidDualSenseGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R3, ElemInputId_GamepadRightStickButton); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickY, hidData->PreviousLeftStickY, ElemInputId_GamepadLeftStickYNegative, ElemInputId_GamepadLeftStickYPositive); + + auto rightStickX = NormalizeInputValueSigned(inputReport->RightStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickX, hidData->PreviousRightStickX, ElemInputId_GamepadRightStickXNegative, ElemInputId_GamepadRightStickXPositive); + + auto rightStickY = -NormalizeInputValueSigned(inputReport->RightStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickY, hidData->PreviousRightStickY, ElemInputId_GamepadRightStickYNegative, ElemInputId_GamepadRightStickYPositive); + + auto leftTrigger = NormalizeInputValue(inputReport->LeftTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, leftTrigger, hidData->PreviousLeftTrigger, ElemInputId_GamepadLeftTrigger); + + auto rightTrigger = NormalizeInputValue(inputReport->RightTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, rightTrigger, hidData->PreviousRightTrigger, ElemInputId_GamepadRightTrigger); + + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Cross, ElemInputId_GamepadButtonA); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Circle, ElemInputId_GamepadButtonB); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Square, ElemInputId_GamepadButtonX); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Triangle, ElemInputId_GamepadButtonY); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L1, ElemInputId_GamepadLeftShoulder); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R1, ElemInputId_GamepadRightShoulder); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Share, ElemInputId_GamepadButtonShare); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Options, ElemInputId_GamepadButtonOptions); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L3, ElemInputId_GamepadLeftStickButton); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R3, ElemInputId_GamepadRightStickButton); + + // TODO: System button // TODO: TESTING ONLY if (inputReport->Buttons & 0x01) @@ -229,6 +263,10 @@ inline void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow wi hidData->PreviousLeftStickX = leftStickX; hidData->PreviousLeftStickY = leftStickY; + hidData->PreviousRightStickX = rightStickX; + hidData->PreviousRightStickY = rightStickY; + hidData->PreviousLeftTrigger = leftTrigger; + hidData->PreviousRightTrigger = rightTrigger; hidData->PreviousButtons = inputReport->Buttons; } } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index a8c89ab0..a61757ff 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -1335,17 +1335,17 @@ typedef enum ElemInputId_MouseWheelPositive = 115, ElemInputId_MouseHorizontalWheelNegative = 116, ElemInputId_MouseHorizontalWheelPositive = 117, - ElemInputID_GamepadButtonA = 118, - ElemInputID_GamepadButtonB = 119, - ElemInputID_GamepadButtonX = 120, - ElemInputID_GamepadButtonY = 121, - ElemInputID_GamepadButtonShare = 122, - ElemInputID_GamepadButtonOptions = 123, - ElemInputID_GamepadButtonHome = 124, - ElemInputID_GamepadLeftShoulder = 125, - ElemInputID_GamepadRightShoulder = 126, - ElemInputID_GamepadLeftTrigger = 127, - ElemInputID_GamepadRightTrigger = 128, + ElemInputId_GamepadButtonA = 118, + ElemInputId_GamepadButtonB = 119, + ElemInputId_GamepadButtonX = 120, + ElemInputId_GamepadButtonY = 121, + ElemInputId_GamepadButtonShare = 122, + ElemInputId_GamepadButtonOptions = 123, + ElemInputId_GamepadButtonHome = 124, + ElemInputId_GamepadLeftShoulder = 125, + ElemInputId_GamepadRightShoulder = 126, + ElemInputId_GamepadLeftTrigger = 127, + ElemInputId_GamepadRightTrigger = 128, ElemInputId_GamepadLeftStickXNegative = 129, ElemInputId_GamepadLeftStickXPositive = 130, ElemInputId_GamepadLeftStickYNegative = 131, diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index c7cc092b..384e40a9 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -388,8 +388,8 @@ ElemPipelineState DirectX12CompileGraphicsPipelineState(ElemGraphicsDevice graph // TODO: Test for now without stack auto stackMemoryArena = SystemGetStackMemoryArena(); - auto stateSubObjects = SystemPushArray(stackMemoryArena, 32); - auto stateSubObjectsIndex = 0u; + //auto stateSubObjects = SystemPushArray(stackMemoryArena, 32); + //auto stateSubObjectsIndex = 0u; auto dxilLibraries = SystemPushArray(stackMemoryArena, 8); auto dxilLibrariesIndex = 0u; @@ -594,6 +594,7 @@ void DirectX12BindPipelineState(ElemCommandList commandList, ElemPipelineState p auto pipelineStateData = GetDirectX12PipelineStateData(pipelineState); SystemAssert(pipelineStateData); + /* D3D12_SET_PROGRAM_DESC programDesc = { .Type = D3D12_PROGRAM_TYPE_GENERIC_PIPELINE, @@ -601,7 +602,7 @@ void DirectX12BindPipelineState(ElemCommandList commandList, ElemPipelineState p { .ProgramIdentifier = pipelineStateData->ProgramIdentifier } - }; + };*/ commandListData->PipelineStateType = pipelineStateData->PipelineStateType; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index 00de0c77..cecf1215 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -467,7 +467,7 @@ void DirectX12PresentSwapChain(ElemSwapChain swapChain) // TODO: Compute vsync interval based on target fps (rework that, it only works for multiples for now!) // TODO: We need to take into account the current present time with the nextPresentTime computed from update function // For example, if we present and we missed a vsync, the calculation should take that into account - auto vsyncInteval = windowData->MonitorRefreshRate / swapChainData->TargetFPS; + //auto vsyncInteval = windowData->MonitorRefreshRate / swapChainData->TargetFPS; // TODO: Control the next vsync for frame pacing (eg: running at 30fps on a 120hz screen) //AssertIfFailed(swapChainData->DeviceObject->Present(vsyncInteval, 0)); From 71117aedc85c883c36e7275c926380331cbd14d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 6 Dec 2024 15:31:14 +0100 Subject: [PATCH 32/93] Update DualSense code --- external/Vulkan-Headers | 2 +- external/volk | 2 +- samples/Common/SampleModelViewerInputs.h | 30 +- samples/Elemental/03-HelloInputs/main.c | 2 + src/Elemental/Common/Inputs/HidDevices.cpp | 7 +- .../Common/Inputs/HidDualSenseGamepad.cpp | 588 ++++++++++++++++++ .../Common/Inputs/HidDualSenseGamepad.h | 347 ----------- ...idSwitchGamepad.h => HidSwitchGamepad.cpp} | 0 src/Elemental/Common/Inputs/HidUtils.h | 92 +++ src/Elemental/Common/Inputs/Inputs.cpp | 1 + src/Elemental/Elemental.h | 29 +- 11 files changed, 735 insertions(+), 365 deletions(-) create mode 100644 src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp delete mode 100644 src/Elemental/Common/Inputs/HidDualSenseGamepad.h rename src/Elemental/Common/Inputs/{HidSwitchGamepad.h => HidSwitchGamepad.cpp} (100%) diff --git a/external/Vulkan-Headers b/external/Vulkan-Headers index 577baa05..29f979ee 160000 --- a/external/Vulkan-Headers +++ b/external/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 577baa05033cf1d9236b3d078ca4b3269ed87a2b +Subproject commit 29f979ee5aa58b7b005f805ea8df7a855c39ff37 diff --git a/external/volk b/external/volk index 01986ac8..59d26900 160000 --- a/external/volk +++ b/external/volk @@ -1 +1 @@ -Subproject commit 01986ac85fa2e5c70df09aeae9c907e27c5d50b2 +Subproject commit 59d26900f53c7621a8ba8ab0e3f18d3bd883fa9a diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleModelViewerInputs.h index 47fa2006..9c2f5c62 100644 --- a/samples/Common/SampleModelViewerInputs.h +++ b/samples/Common/SampleModelViewerInputs.h @@ -39,6 +39,14 @@ typedef struct float TouchRotateSide; float Action; + + float GyroRotate; + float GyroRotateXNegative; + float GyroRotateXPositive; + float GyroRotateYNegative; + float GyroRotateYPositive; + float GyroRotateZNegative; + float GyroRotateZPositive; } SampleModelViewerInputActions; typedef struct @@ -61,6 +69,8 @@ typedef struct void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) { + // TODO: Review rotation signs + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyD, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyW, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); @@ -78,11 +88,20 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateLeft); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateRight); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateUp); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.TouchRotateDown); // TODO: It seems inverted SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseWheelPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseWheelNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseMiddleButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + // TODO: Be careful because it can cause side issues with the touch controls + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonX, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotate); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateYNegative); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateYPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateXNegative); // TODO: It seems inverted + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateXPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityZNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateZNegative); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityZPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateZPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); @@ -92,6 +111,8 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightTrigger, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightShoulder, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadDpadDown, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomOut); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadDpadUp, 0, SampleInputActionBindingType_Value, &inputs->InputActions.ZoomIn); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Touch); @@ -117,6 +138,7 @@ void SampleModelViewerInputsResetTouchParameters(SampleModelViewerInputState* st void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewerInputs* inputs, float deltaTimeInSeconds) { + // TODO: Review rotation order in left handed SampleModelViewerInputState* state = &inputs->State; SampleModelViewerInputActions* inputActions = &inputs->InputActions; state->RotationDelta = V3Zero; @@ -167,6 +189,12 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe state->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; state->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } + else if (inputActions->GyroRotate) + { + state->RotationDelta.X = -(inputActions->GyroRotateXPositive - inputActions->GyroRotateXNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? + state->RotationDelta.Y = -(inputActions->GyroRotateYPositive - inputActions->GyroRotateYNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? + state->RotationDelta.Z = (inputActions->GyroRotateZPositive - inputActions->GyroRotateZNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + } else { SampleVector3 direction = SampleNormalizeV3((SampleVector3) diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 651d8b12..5ec150f8 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -3,6 +3,8 @@ #include "SampleUtils.h" #include "SampleMath.h" +// TODO: Align sample with the Sample inputs header + #define ROTATION_TOUCH_DECREASE_SPEED 0.001f #define ROTATION_TOUCH_SPEED 4.0f #define ROTATION_TOUCH_MAX_DELTA 0.3f diff --git a/src/Elemental/Common/Inputs/HidDevices.cpp b/src/Elemental/Common/Inputs/HidDevices.cpp index 3cc54530..f91380fa 100644 --- a/src/Elemental/Common/Inputs/HidDevices.cpp +++ b/src/Elemental/Common/Inputs/HidDevices.cpp @@ -1,7 +1,9 @@ +#include "HidSwitchGamepad.cpp" +#include "HidDualSenseGamepad.cpp" + +// TODO: Get rid of the headers here. We should only include the cpp files #include "HidDevices.h" #include "HidUtils.h" -#include "HidSwitchGamepad.h" -#include "HidDualSenseGamepad.h" #include "Inputs.h" #include "SystemFunctions.h" @@ -328,6 +330,7 @@ HidGamepadHandler HidGamepadHandlers[] = { .Vendor = HidGamepadVendor_Nintendo, .Product = HidGamepadProduct_SwitchPro, .ProcessDataHandler = ProcessHidSwitchGamepadInputReport } }; +// TODO: Allow each providers to handle registration bool IsHidDeviceSupported(uint32_t vendorId, uint32_t productId) { for (uint32_t i = 0; i < ARRAYSIZE(HidGamepadHandlers); i++) diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp new file mode 100644 index 00000000..ecbabbe0 --- /dev/null +++ b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp @@ -0,0 +1,588 @@ +#pragma once + +#include "../Elemental.h" +#include "Inputs.h" +#include "HidUtils.h" +#include "SystemFunctions.h" +#include "SystemDataPool.h" + +// Based on the spec: https://controllers.fandom.com/wiki/Sony_DualSense + +#define HID_DUALSENSE_STICK_RANGE 255 +#define HID_DUALSENSE_STICK_DEAD_ZONE 0.1f +#define HID_DUALSENSE_TOUCH_MAX_FINGERS 2 +#define HID_DUALSENSE_TOUCH_DEAD_ZONE 0.75f +#define HID_DUALSENSE_ANGULAR_VELOCITY_DEAD_ZONE 0.1f + +enum HidDualSenseGamepadInputReportType +{ + HidDualSenseGamepadInputReportType_BluetoothStandard = 0x01, + HidDualSenseGamepadInputReportType_BluetoothExtended = 0x31, + //HidDualSenseGamepadInputReportType_SubCommand = 0x21 +}; + +enum HidDualSenseGamepadOutputReportType +{ + //HidDualSenseGamepadOutputReportType_SubCommand = 0x01 +}; + +/* +enum HidDualSenseGamepadSubCommandType : uint8_t +{ + HidDualSenseGamepadSubCommandType_DeviceInfo = 0x02 +}; + +enum HidDualSenseGamepadControllerType : uint8_t +{ + HidDualSenseGamepadControllerType_LeftJoyCon = 0x01, + HidDualSenseGamepadControllerType_RightJoyCon = 0x02, + HidDualSenseGamepadControllerType_ProController = 0x03, +};*/ + +enum HidDualSenseGamepadButton : uint16_t +{ + HidDualSenseGamepadButton_DpadUp = 1 << 0, + HidDualSenseGamepadButton_DpadRight = 1 << 1, + HidDualSenseGamepadButton_DpadDown = 1 << 2, + HidDualSenseGamepadButton_DpadLeft = 1 << 3, + HidDualSenseGamepadButton_Square = 1 << 4, + HidDualSenseGamepadButton_Cross = 1 << 5, + HidDualSenseGamepadButton_Circle = 1 << 6, + HidDualSenseGamepadButton_Triangle = 1 << 7, + HidDualSenseGamepadButton_L1 = 1 << 8, + HidDualSenseGamepadButton_R1 = 1 << 9, + HidDualSenseGamepadButton_L2 = 1 << 10, + HidDualSenseGamepadButton_R2 = 1 << 11, + HidDualSenseGamepadButton_Share = 1 << 12, + HidDualSenseGamepadButton_Options = 1 << 13, + HidDualSenseGamepadButton_L3 = 1 << 14, + HidDualSenseGamepadButton_R3 = 1 << 15 +}; + +enum HidDualSenseGamepadSystemButton : uint8_t +{ + HidDualSenseGamepadSystemButton_Home = 1 << 0, + HidDualSenseGamepadSystemButton_Pad = 1 << 1, +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadTouchData +{ + uint32_t PackedData[2]; + uint8_t Timestamp; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadInputReport +{ + uint8_t LeftStickX; + uint8_t LeftStickY; + uint8_t RightStickX; + uint8_t RightStickY; + uint8_t LeftTrigger; + uint8_t RightTrigger; + uint8_t SequenceNumber; + uint16_t Buttons; + uint8_t SystemButtons; + uint8_t Reserved1; + uint32_t Reserved2; + int16_t AngularVelocityX; + int16_t AngularVelocityY; + int16_t AngularVelocityZ; + int16_t AccelerometerX; + int16_t AccelerometerY; + int16_t AccelerometerZ; + uint32_t SensorTimestamp; + int8_t Temperature; + HidDualSenseGamepadTouchData TouchData; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadInputReportBluetoothStandard +{ + uint8_t LeftStickX; + uint8_t LeftStickY; + uint8_t RightStickX; + uint8_t RightStickY; + uint16_t Buttons; + uint8_t SystemButtons; + uint8_t LeftTrigger; + uint8_t RightTrigger; +}; + +/* +struct __attribute__((__packed__)) HidDualSenseGamepadInputReportDeviceInfo +{ + uint16_t FirmwareVersion; + HidDualSenseGamepadControllerType ControllerType; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadOutputReportSubCommand +{ + uint8_t ReportId; + uint8_t GlobalPacketNumber; + // TODO: Rumble Data + uint8_t RumbleData[8]; + HidDualSenseGamepadSubCommandType SubCommandId; + uint8_t Data[53]; +}; + +struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportSetupMemoryRead +{ + uint8_t ReportId; + uint32_t Address; + uint16_t Size; + uint8_t Checksum; +};*/ + +struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportCalibration +{ + uint8_t ReportId; + int16_t GyroPitchBias; + int16_t GyroYawBias; + int16_t GyroRollBias; + int16_t GyroPitchPlus; + int16_t GyroPitchMinus; + int16_t GyroYawPlus; + int16_t GyroYawMinus; + int16_t GyroRollPlus; + int16_t GyroRollMinus; + int16_t GyroSpeedPlus; + int16_t GyroSpeedMinus; + int16_t AccelXPlus; + int16_t AccelXMinus; + int16_t AccelYPlus; + int16_t AccelYMinus; + int16_t AccelZPlus; + int16_t AccelZMinus; + int64_t Unknown; +}; + +struct HidDualSenseGamepadTouchState +{ + uint8_t FingerIndex; + bool IsTouching; + int32_t PreviousPositionX; + int32_t PreviousPositionY; + float PreviousDeltaX; + uint8_t PreviousDeltaXSteps; + float PreviousDeltaY; + uint8_t PreviousDeltaYSteps; +}; + +struct HidDualSenseGamepadData +{ + float PreviousLeftStickX; + float PreviousLeftStickY; + float PreviousRightStickX; + float PreviousRightStickY; + float PreviousLeftTrigger; + float PreviousRightTrigger; + uint16_t PreviousButtons; + uint8_t PreviousMappedDpad; + uint8_t PreviousSystemButtons; + HidDualSenseGamepadTouchState TouchState[HID_DUALSENSE_TOUCH_MAX_FINGERS]; + HidDualSenseGamepadFeatureReportCalibration CalibrationData; +}; + +static SystemDataPool hidDualSenseGamepadDataPool; + +void InitHidDualSenseGamepadMemory() +{ + if (hidDualSenseGamepadDataPool.Storage == nullptr) + { + hidDualSenseGamepadDataPool = SystemCreateDataPool(InputsMemoryArena, MAX_INPUT_DEVICES); + } +} + +HidDualSenseGamepadData* GetHidDualSenseGamepadData(ElemHandle hidDevice) +{ + return SystemGetDataPoolItem(hidDualSenseGamepadDataPool, hidDevice); +} + +/* +bool SendHidDualSenseGamepadSubCommand(ElemInputDevice inputDevice, HidDualSenseGamepadSubCommandType subCommandType, ReadOnlySpan data) +{ + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + HidDualSenseGamepadOutputReportSubCommand outputReport = + { + .ReportId = HidDualSenseGamepadOutputReportType_SubCommand, + .GlobalPacketNumber = hidData->CurrentOutputPacketNumber, + .SubCommandId = subCommandType + }; + + hidData->CurrentOutputPacketNumber = (hidData->CurrentOutputPacketNumber + 1) % 255; + + return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); +}*/ + +uint8_t MapHidDualSenseGamepadDpad(uint8_t dpadData) +{ + static const uint8_t mapping[8] = { + HidDualSenseGamepadButton_DpadUp, + HidDualSenseGamepadButton_DpadUp | HidDualSenseGamepadButton_DpadRight, + HidDualSenseGamepadButton_DpadRight, + HidDualSenseGamepadButton_DpadRight | HidDualSenseGamepadButton_DpadDown, + HidDualSenseGamepadButton_DpadDown, + HidDualSenseGamepadButton_DpadDown | HidDualSenseGamepadButton_DpadLeft, + HidDualSenseGamepadButton_DpadLeft, + HidDualSenseGamepadButton_DpadLeft | HidDualSenseGamepadButton_DpadUp + }; + + return (dpadData & 8) ? 0 : mapping[dpadData % 8]; +} + +void ParseHidDualSenseGamepadTouchData(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, HidDualSenseGamepadTouchState* touchState, uint8_t fingerIndex, uint32_t touchData) +{ + // TODO: Replace Magic numbers with constant values + + auto fingerId = (touchData >> 0) & 0x7F; + auto isTouching = !((touchData >> 7) & 0x01); + auto positionX = (int32_t)((touchData >> 8) & 0xFFF); + auto positionY = (int32_t)((touchData >> 20) & 0xFFF); + + auto deltaX = 0.0f; + auto deltaY = 0.0f; + + if (isTouching != touchState->IsTouching) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_Touch, + .InputType = ElemInputType_Digital, + .Value = isTouching ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + if (!isTouching) + { + if (touchState->PreviousDeltaXSteps < 50) + { + deltaX = touchState->PreviousDeltaX; + } + + if (touchState->PreviousDeltaYSteps < 50) + { + deltaY = touchState->PreviousDeltaY; + } + } + } + else + { + deltaX = (float)(positionX - touchState->PreviousPositionX) * 0.15f; + deltaY = (float)(positionY - touchState->PreviousPositionY) * 2.0f * 0.15f; + } + + if (deltaX < -HID_DUALSENSE_TOUCH_DEAD_ZONE) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchXNegative, + .InputType = ElemInputType_Delta, + .Value = -deltaX, + .ElapsedSeconds = elapsedSeconds + }); + + touchState->PreviousDeltaX = deltaX; + touchState->PreviousDeltaXSteps = 0; + } + + if (deltaX > HID_DUALSENSE_TOUCH_DEAD_ZONE) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchXPositive, + .InputType = ElemInputType_Delta, + .Value = deltaX, + .ElapsedSeconds = elapsedSeconds + }); + + touchState->PreviousDeltaX = deltaX; + touchState->PreviousDeltaXSteps = 0; + } + + if (deltaY < -HID_DUALSENSE_TOUCH_DEAD_ZONE) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchYNegative, + .InputType = ElemInputType_Delta, + .Value = -deltaY, + .ElapsedSeconds = elapsedSeconds + }); + + touchState->PreviousDeltaY = deltaY; + touchState->PreviousDeltaYSteps = 0; + } + + if (deltaY > HID_DUALSENSE_TOUCH_DEAD_ZONE) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchYPositive, + .InputType = ElemInputType_Delta, + .Value = deltaY, + .ElapsedSeconds = elapsedSeconds + }); + + touchState->PreviousDeltaY = deltaY; + touchState->PreviousDeltaYSteps = 0; + } + + // TODO: Replace magic numbers + if (touchState->PreviousDeltaXSteps == 0 || (isTouching != touchState->IsTouching)) + { + auto normalizedPositionX = (float)positionX / 1920.0f; + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchXAbsolutePosition, + .InputType = ElemInputType_Absolute, + .Value = normalizedPositionX, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (touchState->PreviousDeltaYSteps == 0 || (isTouching != touchState->IsTouching)) + { + auto normalizedPositionY = (float)positionY / 1070.0f; + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputDeviceTypeIndex = fingerIndex, + .InputId = ElemInputId_TouchYAbsolutePosition, + .InputType = ElemInputType_Absolute, + .Value = -normalizedPositionY, + .ElapsedSeconds = elapsedSeconds + }); + } + + touchState->IsTouching = isTouching; + touchState->PreviousPositionX = positionX; + touchState->PreviousPositionY = positionY; + + if (touchState->PreviousDeltaXSteps < 255) + { + touchState->PreviousDeltaXSteps += 1; + } + + if (touchState->PreviousDeltaYSteps < 255) + { + touchState->PreviousDeltaYSteps += 1; + } +} + +float NormalizeHidDualSenseGamepadAngularVelocity(int16_t value, int16_t bias, int16_t thresholdMinus, int16_t thresholdPlus, int16_t speedMinus, int16_t speedPlus) +{ + float normalizedValue; + + if (value < 0) + { + normalizedValue = (float)(SystemMax(value, thresholdMinus) + bias) / speedMinus; + } + else + { + normalizedValue = (float)(SystemMin(value, thresholdPlus) + bias) / speedPlus; + } + + if (normalizedValue < HID_DUALSENSE_ANGULAR_VELOCITY_DEAD_ZONE && normalizedValue > -HID_DUALSENSE_ANGULAR_VELOCITY_DEAD_ZONE) + { + return 0.0f; + } + + return normalizedValue; +} + +void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReport* inputReport, double elapsedSeconds) +{ + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + if (inputDeviceData->HidDeviceData != ELEM_HANDLE_NULL) + { + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + auto leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickX, hidData->PreviousLeftStickX, ElemInputId_GamepadLeftStickXNegative, ElemInputId_GamepadLeftStickXPositive); + + auto leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickY, hidData->PreviousLeftStickY, ElemInputId_GamepadLeftStickYNegative, ElemInputId_GamepadLeftStickYPositive); + + auto rightStickX = NormalizeInputValueSigned(inputReport->RightStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickX, hidData->PreviousRightStickX, ElemInputId_GamepadRightStickXNegative, ElemInputId_GamepadRightStickXPositive); + + auto rightStickY = -NormalizeInputValueSigned(inputReport->RightStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickY, hidData->PreviousRightStickY, ElemInputId_GamepadRightStickYNegative, ElemInputId_GamepadRightStickYPositive); + + auto leftTrigger = NormalizeInputValue(inputReport->LeftTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, leftTrigger, hidData->PreviousLeftTrigger, ElemInputId_GamepadLeftTrigger); + + auto rightTrigger = NormalizeInputValue(inputReport->RightTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); + ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, rightTrigger, hidData->PreviousRightTrigger, ElemInputId_GamepadRightTrigger); + + auto mappedDpad = MapHidDualSenseGamepadDpad(inputReport->Buttons); + + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, mappedDpad, hidData->PreviousMappedDpad, HidDualSenseGamepadButton_DpadUp, ElemInputId_GamepadDpadUp); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, mappedDpad, hidData->PreviousMappedDpad, HidDualSenseGamepadButton_DpadRight, ElemInputId_GamepadDpadRight); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, mappedDpad, hidData->PreviousMappedDpad, HidDualSenseGamepadButton_DpadDown, ElemInputId_GamepadDpadDown); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, mappedDpad, hidData->PreviousMappedDpad, HidDualSenseGamepadButton_DpadLeft, ElemInputId_GamepadDpadLeft); + + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Cross, ElemInputId_GamepadButtonA); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Circle, ElemInputId_GamepadButtonB); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Square, ElemInputId_GamepadButtonX); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Triangle, ElemInputId_GamepadButtonY); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L1, ElemInputId_GamepadLeftShoulder); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R1, ElemInputId_GamepadRightShoulder); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Share, ElemInputId_GamepadButtonShare); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Options, ElemInputId_GamepadButtonOptions); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L3, ElemInputId_GamepadLeftStickButton); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R3, ElemInputId_GamepadRightStickButton); + + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->SystemButtons, hidData->PreviousSystemButtons, HidDualSenseGamepadSystemButton_Home, ElemInputId_GamepadButtonHome); + ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->SystemButtons, hidData->PreviousSystemButtons, HidDualSenseGamepadSystemButton_Pad, ElemInputId_GamepadTouchButton); + + // TODO: DualSense Edge buttons? + + // TODO: Find another way to check if we are in advanced format + if (hidData->CalibrationData.AccelXPlus != 0) + { + for (uint32_t i = 0; i < HID_DUALSENSE_TOUCH_MAX_FINGERS; i++) + { + ParseHidDualSenseGamepadTouchData(window, inputDevice, elapsedSeconds, &hidData->TouchState[i], i, inputReport->TouchData.PackedData[i]); + } + } + + auto calibration = hidData->CalibrationData; + + auto angularVelocityX = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityX, calibration.GyroPitchBias, calibration.GyroPitchMinus, calibration.GyroPitchPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityX, ElemInputId_GamepadAngularVelocityXNegative, ElemInputId_GamepadAngularVelocityXPositive); + + auto angularVelocityY = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityY, calibration.GyroYawBias, calibration.GyroYawMinus, calibration.GyroYawPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityY, ElemInputId_GamepadAngularVelocityYNegative, ElemInputId_GamepadAngularVelocityYPositive); + + auto angularVelocityZ = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityZ, calibration.GyroRollBias, calibration.GyroRollMinus, calibration.GyroRollPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityZ, ElemInputId_GamepadAngularVelocityZNegative, ElemInputId_GamepadAngularVelocityZPositive); + + hidData->PreviousLeftStickX = leftStickX; + hidData->PreviousLeftStickY = leftStickY; + hidData->PreviousRightStickX = rightStickX; + hidData->PreviousRightStickY = rightStickY; + hidData->PreviousLeftTrigger = leftTrigger; + hidData->PreviousRightTrigger = rightTrigger; + hidData->PreviousButtons = inputReport->Buttons; + hidData->PreviousMappedDpad = mappedDpad; + hidData->PreviousSystemButtons = inputReport->SystemButtons; + } +} + +void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReportBluetoothStandard* inputReport, double elapsedSeconds) +{ + //SystemLogWarningMessage(ElemLogMessageCategory_Inputs, "Using DualSense GamePad Bluetooth Standard Report %f", elapsedSeconds); + + HidDualSenseGamepadInputReport mappedInputReport = + { + .LeftStickX = inputReport->LeftStickX, + .LeftStickY = inputReport->LeftStickY, + .RightStickX = inputReport->RightStickX, + .RightStickY = inputReport->RightStickY, + .LeftTrigger = inputReport->LeftTrigger, + .RightTrigger = inputReport->RightTrigger, + .Buttons = inputReport->Buttons, + .SystemButtons = inputReport->SystemButtons + }; + + ProcessHidDualSenseGamepadInputReport(window, inputDevice, &mappedInputReport, elapsedSeconds); +} + +void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + // TODO: We need to differentiate USB / Bluetooth? How can we do that? + + InitHidDualSenseGamepadMemory(); + + auto inputDeviceData = GetInputDeviceData(inputDevice); + SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); + + // TODO: This part is to get the calibration data + if (inputDeviceData->HidDeviceData == ELEM_HANDLE_NULL) + { + /* + // Read Config + HidDualSenseGamepadFeatureReportSetupMemoryRead featureReport = + { + .ReportId = 0x71, + .Address = 0xF8000000 + 0x6000, + .Size = 0xA000, + .Checksum = 0 + }; + + auto pointer = (uint8_t*)&featureReport; + auto sumBytes = 0u; + + for (uint32_t i = 0; i < sizeof(featureReport); i++) + { + sumBytes += pointer[i]; + } + + featureReport.Checksum = 0x100 - sumBytes; + + auto result = PlatformHidSendFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&featureReport, sizeof(featureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Send Feature Result: %d", result); +*/ + HidDualSenseGamepadFeatureReportCalibration calibrationFeatureReport = + { + // TODO: Replace with enum + .ReportId = 0x05, + }; + + auto result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&calibrationFeatureReport, sizeof(calibrationFeatureReport))); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result); + + inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidDualSenseGamepadDataPool, { .CalibrationData = calibrationFeatureReport }); + } + + auto reportId = hidReport[0]; + + if (reportId == HidDualSenseGamepadInputReportType_BluetoothStandard) + { + auto inputReport = (HidDualSenseGamepadInputReportBluetoothStandard*)(++hidReport.Pointer); + ProcessHidDualSenseGamepadInputReportBluetoothStandard(window, inputDevice, inputReport, elapsedSeconds); + } + else if (reportId == HidDualSenseGamepadInputReportType_BluetoothExtended) + { + auto inputReport = (HidDualSenseGamepadInputReport*)(hidReport.Pointer + 2); + ProcessHidDualSenseGamepadInputReport(window, inputDevice, inputReport, elapsedSeconds); + } + /* + else if (reportId == HidDualSenseGamepadInputReportType_SubCommand) + { + auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidData); + + + auto inputReport = (HidDualSenseGamepadInputReportExtended*)(++hidReport.Pointer); + + uint8_t *data = inputReport->LeftStick; + uint16_t stick_horizontal = data[0] | ((data[1] & 0xF) << 8); + uint16_t stick_vertical = (data[1] >> 4) | (data[2] << 4); + + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Report: Timer=%d, X=%d, Y=%d", inputReport->Timer, stick_horizontal, stick_vertical); + //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); + }*/ +} diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.h b/src/Elemental/Common/Inputs/HidDualSenseGamepad.h deleted file mode 100644 index 6e2163d4..00000000 --- a/src/Elemental/Common/Inputs/HidDualSenseGamepad.h +++ /dev/null @@ -1,347 +0,0 @@ -#pragma once - -#include "../Elemental.h" -#include "Inputs.h" -#include "HidUtils.h" -#include "SystemFunctions.h" -#include "SystemDataPool.h" - -// Based on the spec: https://controllers.fandom.com/wiki/Sony_DualSense - -#define HID_DUALSENSE_STICK_RANGE 255 -#define HID_DUALSENSE_STICK_DEAD_ZONE 0.1f - -enum HidDualSenseGamepadInputReportType -{ - HidDualSenseGamepadInputReportType_BluetoothStandard = 0x01, - //HidDualSenseGamepadInputReportType_SubCommand = 0x21 -}; - -enum HidDualSenseGamepadOutputReportType -{ - //HidDualSenseGamepadOutputReportType_SubCommand = 0x01 -}; - -/* -enum HidDualSenseGamepadSubCommandType : uint8_t -{ - HidDualSenseGamepadSubCommandType_DeviceInfo = 0x02 -}; - -enum HidDualSenseGamepadControllerType : uint8_t -{ - HidDualSenseGamepadControllerType_LeftJoyCon = 0x01, - HidDualSenseGamepadControllerType_RightJoyCon = 0x02, - HidDualSenseGamepadControllerType_ProController = 0x03, -};*/ - -enum HidDualSenseGamepadButton : uint16_t -{ - HidDualSenseGamepadButton_Square = 1 << 4, - HidDualSenseGamepadButton_Cross = 1 << 5, - HidDualSenseGamepadButton_Circle = 1 << 6, - HidDualSenseGamepadButton_Triangle = 1 << 7, - HidDualSenseGamepadButton_L1 = 1 << 8, - HidDualSenseGamepadButton_R1 = 1 << 9, - HidDualSenseGamepadButton_L2 = 1 << 10, - HidDualSenseGamepadButton_R2 = 1 << 11, - HidDualSenseGamepadButton_Share = 1 << 12, - HidDualSenseGamepadButton_Options = 1 << 13, - HidDualSenseGamepadButton_L3 = 1 << 14, - HidDualSenseGamepadButton_R3 = 1 << 15 -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadInputReportBluetoothStandard -{ - uint8_t LeftStickX; - uint8_t LeftStickY; - uint8_t RightStickX; - uint8_t RightStickY; - uint16_t Buttons; - uint8_t SystemButtons; - uint8_t LeftTrigger; - uint8_t RightTrigger; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadInputReportExtended -{ - uint8_t Timer; - uint8_t BatteryConnection; - uint8_t Buttons[3]; - uint8_t LeftStick[3]; - uint8_t RightStick[3]; -}; - -struct HidDualSenseGamepadData -{ - float PreviousLeftStickX; - float PreviousLeftStickY; - float PreviousRightStickX; - float PreviousRightStickY; - float PreviousLeftTrigger; - float PreviousRightTrigger; - uint16_t PreviousButtons; -}; - -/* -struct __attribute__((__packed__)) HidDualSenseGamepadInputReportDeviceInfo -{ - uint16_t FirmwareVersion; - HidDualSenseGamepadControllerType ControllerType; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadOutputReportSubCommand -{ - uint8_t ReportId; - uint8_t GlobalPacketNumber; - // TODO: Rumble Data - uint8_t RumbleData[8]; - HidDualSenseGamepadSubCommandType SubCommandId; - uint8_t Data[53]; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportSetupMemoryRead -{ - uint8_t ReportId; - uint32_t Address; - uint16_t Size; - uint8_t Checksum; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportMemoryRead -{ - uint8_t ReportId; - uint8_t Checksum; - uint8_t Data[1024]; -};*/ - -static SystemDataPool hidDualSenseGamepadDataPool; - -inline void InitHidDualSenseGamepadMemory() -{ - if (hidDualSenseGamepadDataPool.Storage == nullptr) - { - hidDualSenseGamepadDataPool = SystemCreateDataPool(InputsMemoryArena, MAX_INPUT_DEVICES); - } -} - -inline HidDualSenseGamepadData* GetHidDualSenseGamepadData(ElemHandle hidDevice) -{ - return SystemGetDataPoolItem(hidDualSenseGamepadDataPool, hidDevice); -} - -/* -inline bool SendHidDualSenseGamepadSubCommand(ElemInputDevice inputDevice, HidDualSenseGamepadSubCommandType subCommandType, ReadOnlySpan data) -{ - auto inputDeviceData = GetInputDeviceData(inputDevice); - SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); - - auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); - SystemAssert(hidData); - - HidDualSenseGamepadOutputReportSubCommand outputReport = - { - .ReportId = HidDualSenseGamepadOutputReportType_SubCommand, - .GlobalPacketNumber = hidData->CurrentOutputPacketNumber, - .SubCommandId = subCommandType - }; - - hidData->CurrentOutputPacketNumber = (hidData->CurrentOutputPacketNumber + 1) % 255; - - return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); -}*/ - -// TODO: Promote those functions to common header -inline void ProcessHidGamepadStick(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float stickData, float previousStickData, ElemInputId negativeInputId, ElemInputId positiveInputId) -{ - if (stickData != previousStickData) - { - if (stickData <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = negativeInputId, - .InputType = ElemInputType_Analog, - .Value = -stickData, - .ElapsedSeconds = elapsedSeconds - }); - } - - if (stickData >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = positiveInputId, - .InputType = ElemInputType_Analog, - .Value = stickData, - .ElapsedSeconds = elapsedSeconds - }); - } - } -} - -inline void ProcessHidGamepadTrigger(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float data, float previousData, ElemInputId inputId) -{ - if (data != previousData) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = inputId, - .InputType = ElemInputType_Analog, - .Value = data, - .ElapsedSeconds = elapsedSeconds - }); - } -} - -inline void ProcessHidGamepadButton(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, uint16_t currentButtons, uint16_t previousButtons, uint16_t buttonType, ElemInputId inputId) -{ - if ((currentButtons & buttonType) != (previousButtons & buttonType)) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = inputId, - .InputType = ElemInputType_Digital, - .Value = (currentButtons & buttonType) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - } -} - -inline void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReportBluetoothStandard* inputReport, double elapsedSeconds) -{ - //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "DualSenseGamePad Bluetooth Standard Report %f", elapsedSeconds); - - auto inputDeviceData = GetInputDeviceData(inputDevice); - SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); - - if (inputDeviceData->HidDeviceData != ELEM_HANDLE_NULL) - { - auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); - SystemAssert(hidData); - - auto leftStickX = NormalizeInputValueSigned(inputReport->LeftStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickX, hidData->PreviousLeftStickX, ElemInputId_GamepadLeftStickXNegative, ElemInputId_GamepadLeftStickXPositive); - - auto leftStickY = -NormalizeInputValueSigned(inputReport->LeftStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, leftStickY, hidData->PreviousLeftStickY, ElemInputId_GamepadLeftStickYNegative, ElemInputId_GamepadLeftStickYPositive); - - auto rightStickX = NormalizeInputValueSigned(inputReport->RightStickX, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickX, hidData->PreviousRightStickX, ElemInputId_GamepadRightStickXNegative, ElemInputId_GamepadRightStickXPositive); - - auto rightStickY = -NormalizeInputValueSigned(inputReport->RightStickY, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, rightStickY, hidData->PreviousRightStickY, ElemInputId_GamepadRightStickYNegative, ElemInputId_GamepadRightStickYPositive); - - auto leftTrigger = NormalizeInputValue(inputReport->LeftTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, leftTrigger, hidData->PreviousLeftTrigger, ElemInputId_GamepadLeftTrigger); - - auto rightTrigger = NormalizeInputValue(inputReport->RightTrigger, HID_DUALSENSE_STICK_RANGE, HID_DUALSENSE_STICK_DEAD_ZONE); - ProcessHidGamepadTrigger(window, inputDevice, elapsedSeconds, rightTrigger, hidData->PreviousRightTrigger, ElemInputId_GamepadRightTrigger); - - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Cross, ElemInputId_GamepadButtonA); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Circle, ElemInputId_GamepadButtonB); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Square, ElemInputId_GamepadButtonX); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Triangle, ElemInputId_GamepadButtonY); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L1, ElemInputId_GamepadLeftShoulder); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R1, ElemInputId_GamepadRightShoulder); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Share, ElemInputId_GamepadButtonShare); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_Options, ElemInputId_GamepadButtonOptions); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_L3, ElemInputId_GamepadLeftStickButton); - ProcessHidGamepadButton(window, inputDevice, elapsedSeconds, inputReport->Buttons, hidData->PreviousButtons, HidDualSenseGamepadButton_R3, ElemInputId_GamepadRightStickButton); - - // TODO: System button - - // TODO: TESTING ONLY - if (inputReport->Buttons & 0x01) - { - //SendHidDualSenseGamepadSubCommand(inputDevice, HidDualSenseGamepadSubCommandType_DeviceInfo, ReadOnlySpan()); - } - - hidData->PreviousLeftStickX = leftStickX; - hidData->PreviousLeftStickY = leftStickY; - hidData->PreviousRightStickX = rightStickX; - hidData->PreviousRightStickY = rightStickY; - hidData->PreviousLeftTrigger = leftTrigger; - hidData->PreviousRightTrigger = rightTrigger; - hidData->PreviousButtons = inputReport->Buttons; - } -} - -inline void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) -{ - // TODO: We need to differentiate USB / Bluetooth? How can we do that? - - InitHidDualSenseGamepadMemory(); - - auto inputDeviceData = GetInputDeviceData(inputDevice); - SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); - - // TODO: This part is to get the calibration data - if (inputDeviceData->HidDeviceData == ELEM_HANDLE_NULL) - { - /* - // Read Config - HidDualSenseGamepadFeatureReportSetupMemoryRead featureReport = - { - .ReportId = 0x71, - .Address = 0xF8000000 + 0x6000, - .Size = 0xA000, - .Checksum = 0 - }; - - auto pointer = (uint8_t*)&featureReport; - auto sumBytes = 0u; - - for (uint32_t i = 0; i < sizeof(featureReport); i++) - { - sumBytes += pointer[i]; - } - - featureReport.Checksum = 0x100 - sumBytes; - - auto result = PlatformHidSendFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&featureReport, sizeof(featureReport))); - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Send Feature Result: %d", result); - - HidDualSenseGamepadFeatureReportMemoryRead readFeatureReport = - { - .ReportId = 0x72, - .Checksum = 0 - }; - - result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&readFeatureReport, sizeof(readFeatureReport))); - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result);*/ - - inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidDualSenseGamepadDataPool, {}); - } - - auto reportId = hidReport[0]; - - //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "DualSenseGamePad %d", reportId); - - // TODO: Use the the advanced report - if (reportId == HidDualSenseGamepadInputReportType_BluetoothStandard) - { - auto inputReport = (HidDualSenseGamepadInputReportBluetoothStandard*)(++hidReport.Pointer); - ProcessHidDualSenseGamepadInputReportBluetoothStandard(window, inputDevice, inputReport, elapsedSeconds); - } - /* - else if (reportId == HidDualSenseGamepadInputReportType_SubCommand) - { - auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); - SystemAssert(hidData); - - - auto inputReport = (HidDualSenseGamepadInputReportExtended*)(++hidReport.Pointer); - - uint8_t *data = inputReport->LeftStick; - uint16_t stick_horizontal = data[0] | ((data[1] & 0xF) << 8); - uint16_t stick_vertical = (data[1] >> 4) | (data[2] << 4); - - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Report: Timer=%d, X=%d, Y=%d", inputReport->Timer, stick_horizontal, stick_vertical); - //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); - }*/ -} diff --git a/src/Elemental/Common/Inputs/HidSwitchGamepad.h b/src/Elemental/Common/Inputs/HidSwitchGamepad.cpp similarity index 100% rename from src/Elemental/Common/Inputs/HidSwitchGamepad.h rename to src/Elemental/Common/Inputs/HidSwitchGamepad.cpp diff --git a/src/Elemental/Common/Inputs/HidUtils.h b/src/Elemental/Common/Inputs/HidUtils.h index f8c3ec76..3f58a80c 100644 --- a/src/Elemental/Common/Inputs/HidUtils.h +++ b/src/Elemental/Common/Inputs/HidUtils.h @@ -1,5 +1,10 @@ #pragma once +#include "../Elemental.h" +#include "Inputs.h" + +// TODO: Create a CPP file + inline float NormalizeInputValue(uint32_t value, uint32_t maxValue, float deadZone) { // TODO: Allows the configuration of deadzone @@ -25,3 +30,90 @@ inline float NormalizeInputValueSigned(uint32_t value, uint32_t maxValue, float return normalizedValue; } + +inline void ProcessHidGamepadDeltaAxe(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float axeData, ElemInputId negativeInputId, ElemInputId positiveInputId) +{ + if (axeData < 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = negativeInputId, + .InputType = ElemInputType_Delta, + .Value = -axeData, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (axeData > 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = positiveInputId, + .InputType = ElemInputType_Delta, + .Value = axeData, + .ElapsedSeconds = elapsedSeconds + }); + } +} + +inline void ProcessHidGamepadStick(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float stickData, float previousStickData, ElemInputId negativeInputId, ElemInputId positiveInputId) +{ + if (stickData != previousStickData) + { + if (stickData <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = negativeInputId, + .InputType = ElemInputType_Analog, + .Value = -stickData, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (stickData >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = positiveInputId, + .InputType = ElemInputType_Analog, + .Value = stickData, + .ElapsedSeconds = elapsedSeconds + }); + } + } +} + +inline void ProcessHidGamepadTrigger(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, float data, float previousData, ElemInputId inputId) +{ + if (data != previousData) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = inputId, + .InputType = ElemInputType_Analog, + .Value = data, + .ElapsedSeconds = elapsedSeconds + }); + } +} + +inline void ProcessHidGamepadButton(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, uint16_t currentButtons, uint16_t previousButtons, uint16_t buttonType, ElemInputId inputId) +{ + if ((currentButtons & buttonType) != (previousButtons & buttonType)) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = inputId, + .InputType = ElemInputType_Digital, + .Value = (currentButtons & buttonType) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + } +} diff --git a/src/Elemental/Common/Inputs/Inputs.cpp b/src/Elemental/Common/Inputs/Inputs.cpp index 93f23dc0..8298f032 100644 --- a/src/Elemental/Common/Inputs/Inputs.cpp +++ b/src/Elemental/Common/Inputs/Inputs.cpp @@ -124,6 +124,7 @@ void ResetInputsFrame() for (uint32_t i = 0; i < previousDeltaInputsToResetWriteIndex; i++) { + // TODO: Investigate if we process multiple time the same event?? if (previousDeltaToReset[i].InputId != ElemInputId_Unknown) { auto inputEvent = previousDeltaToReset[i]; diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index a61757ff..799e8f68 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -1360,27 +1360,30 @@ typedef enum ElemInputId_GamepadDpadRight = 140, ElemInputId_GamepadDpadDown = 141, ElemInputId_GamepadDpadLeft = 142, - ElemInputId_Touch = 143, - ElemInputId_TouchXNegative = 144, - ElemInputId_TouchXPositive = 145, - ElemInputId_TouchYNegative = 146, - ElemInputId_TouchYPositive = 147, - ElemInputId_TouchXAbsolutePosition = 148, - ElemInputId_TouchYAbsolutePosition = 149 + ElemInputId_GamepadTouchButton = 143, + ElemInputId_GamepadAngularVelocityXNegative = 144, + ElemInputId_GamepadAngularVelocityXPositive = 145, + ElemInputId_GamepadAngularVelocityYNegative = 146, + ElemInputId_GamepadAngularVelocityYPositive = 147, + ElemInputId_GamepadAngularVelocityZNegative = 148, + ElemInputId_GamepadAngularVelocityZPositive = 149, + ElemInputId_Touch = 150, + ElemInputId_TouchXNegative = 151, + ElemInputId_TouchXPositive = 152, + ElemInputId_TouchYNegative = 153, + ElemInputId_TouchYPositive = 154, + ElemInputId_TouchXAbsolutePosition = 155, + ElemInputId_TouchYAbsolutePosition = 156 } ElemInputId; typedef struct { ElemInputDevice Handle; ElemInputDeviceType DeviceType; + // TODO: Add device name + // TODO: Add device capabilities for gamepad: Gyros, Touchpad, etc. } ElemInputDeviceInfo; -typedef struct -{ - ElemInputDeviceInfo* Items; - uint32_t Length; -} ElemInputDeviceInfoSpan; - typedef struct { ElemWindow Window; From 543e41ada494fd4aed2add7bb1dd0a80da1ab0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 11 Dec 2024 11:55:28 +0100 Subject: [PATCH 33/93] Dotnet update --- .../dotnet/samples/02-HelloWindow/Program.cs | 3 +- .../src/Elemental/ApplicationService.cs | 30 +++++ .../dotnet/src/Elemental/Elemental.csproj | 8 +- .../src/Elemental/RunApplicationParameters.cs | 32 +---- .../dotnet/src/Elemental_REF/Elemental.csproj | 50 -------- external/Vulkan-Headers | 2 +- external/volk | 2 +- src/Elemental/Apple/Inputs.cpp | 24 ++-- utilities/CodeGenerator/CodeGenerator.csproj | 2 +- .../CodeGenerator/DotnetCodeGenerator.cs | 117 +++++++++--------- 10 files changed, 109 insertions(+), 161 deletions(-) delete mode 100644 bindings/dotnet/src/Elemental_REF/Elemental.csproj diff --git a/bindings/dotnet/samples/02-HelloWindow/Program.cs b/bindings/dotnet/samples/02-HelloWindow/Program.cs index a52f9af4..81bbd04a 100644 --- a/bindings/dotnet/samples/02-HelloWindow/Program.cs +++ b/bindings/dotnet/samples/02-HelloWindow/Program.cs @@ -3,6 +3,7 @@ var applicationService = new ApplicationService(); applicationService.ConfigureLogHandler(DefaultLogHandlers.ConsoleLogHandler); +// TODO: Scenario with no payloads applicationService.RunApplication(new () { ApplicationName = "Hello Window"u8, @@ -20,7 +21,7 @@ void InitSample(ref TestPayload payload) Console.WriteLine($"Path: {System.Text.Encoding.UTF8.GetString(systemInfo.ApplicationPath)}"); - payload.Window = applicationService.CreateWindow(); + payload.Window = applicationService.CreateWindow(new WindowOptions { Title = "Test"u8 }); Console.WriteLine($"Test: {payload.Test}"); } diff --git a/bindings/dotnet/src/Elemental/ApplicationService.cs b/bindings/dotnet/src/Elemental/ApplicationService.cs index bff7657a..cb55d807 100644 --- a/bindings/dotnet/src/Elemental/ApplicationService.cs +++ b/bindings/dotnet/src/Elemental/ApplicationService.cs @@ -1,5 +1,35 @@ namespace Elemental; +internal static class InitApplicationHandler where T : unmanaged +{ + public static ApplicationHandler? _interceptorEntry; + + public static void Interceptor(ref T payload) + { + if (_interceptorEntry == null) + { + return; + } + + _interceptorEntry(ref payload); + } +} + +internal static class FreeApplicationHandler where T : unmanaged +{ + public static ApplicationHandler? _interceptorEntry; + + public static void Interceptor(ref T payload) + { + if (_interceptorEntry == null) + { + return; + } + + _interceptorEntry(ref payload); + } +} + /// public class ApplicationService : IApplicationService { diff --git a/bindings/dotnet/src/Elemental/Elemental.csproj b/bindings/dotnet/src/Elemental/Elemental.csproj index bdc1c8e6..e74bb91b 100644 --- a/bindings/dotnet/src/Elemental/Elemental.csproj +++ b/bindings/dotnet/src/Elemental/Elemental.csproj @@ -16,15 +16,15 @@ Elemental.Native.dll PreserveNewest - + Elemental.Native.pdb PreserveNewest - + D3D12Core.dll PreserveNewest - + D3D12SDKLayers.dll PreserveNewest @@ -43,7 +43,7 @@ --> - + diff --git a/bindings/dotnet/src/Elemental/RunApplicationParameters.cs b/bindings/dotnet/src/Elemental/RunApplicationParameters.cs index c7179408..7ef786d2 100644 --- a/bindings/dotnet/src/Elemental/RunApplicationParameters.cs +++ b/bindings/dotnet/src/Elemental/RunApplicationParameters.cs @@ -1,35 +1,5 @@ namespace Elemental; -internal class InitApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - -internal static class FreeApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - /// /// Holds parameters for running an application, including initialization and cleanup routines. /// @@ -64,6 +34,6 @@ internal unsafe struct RunApplicationParametersUnsafe public nint FreeHandler { get; set; } - public nint Payload { get; set; } + public nint Payload; } diff --git a/bindings/dotnet/src/Elemental_REF/Elemental.csproj b/bindings/dotnet/src/Elemental_REF/Elemental.csproj deleted file mode 100644 index e74bb91b..00000000 --- a/bindings/dotnet/src/Elemental_REF/Elemental.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - true - true - true - true - - - - - - - - - Elemental.Native.dll - PreserveNewest - - - Elemental.Native.pdb - PreserveNewest - - - D3D12Core.dll - PreserveNewest - - - D3D12SDKLayers.dll - PreserveNewest - - - - - - - - - - - - diff --git a/external/Vulkan-Headers b/external/Vulkan-Headers index 29f979ee..577baa05 160000 --- a/external/Vulkan-Headers +++ b/external/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 29f979ee5aa58b7b005f805ea8df7a855c39ff37 +Subproject commit 577baa05033cf1d9236b3d078ca4b3269ed87a2b diff --git a/external/volk b/external/volk index 59d26900..01986ac8 160000 --- a/external/volk +++ b/external/volk @@ -1 +1 @@ -Subproject commit 59d26900f53c7621a8ba8ab0e3f18d3bd883fa9a +Subproject commit 01986ac85fa2e5c70df09aeae9c907e27c5d50b2 diff --git a/src/Elemental/Apple/Inputs.cpp b/src/Elemental/Apple/Inputs.cpp index a25f868e..d4078ce1 100644 --- a/src/Elemental/Apple/Inputs.cpp +++ b/src/Elemental/Apple/Inputs.cpp @@ -645,6 +645,7 @@ void InitInputs(ElemWindow window) NS::NotificationCenter::defaultCenter()->addObserver(MTLSTR("GCControllerDidConnectNotification"), nullptr, nullptr, ^(NS::Notification* notification) { + // TODO: Use HID code like the rest of the platforms for gamepad? auto controller = (GC::Controller*)notification->object(); auto extendedGamepad = controller->extendedGamepad(); @@ -656,57 +657,58 @@ void InitInputs(ElemWindow window) extendedGamepad->buttonA()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonA, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonA, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->buttonB()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonB, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonB, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->buttonX()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonX, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonX, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->buttonY()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonY, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonY, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); + // TODO: Check that button ID extendedGamepad->buttonMenu()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonMenu, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonShare, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->buttonOptions()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonOptions, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonOptions, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->buttonHome()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadButtonHome, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadButtonHome, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->leftShoulder()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadLeftShoulder, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadLeftShoulder, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->rightShoulder()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadRightShoulder, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadRightShoulder, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->leftTrigger()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadLeftTrigger, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadLeftTrigger, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->rightTrigger()->setValueChangedHandler(^(GC::ControllerButtonInput* controllerButtonInput, float value, bool isPressed) { - ButtonHandler(window, ElemInputID_GamepadRightTrigger, extendedGamepad->device(), controllerButtonInput, value, isPressed); + ButtonHandler(window, ElemInputId_GamepadRightTrigger, extendedGamepad->device(), controllerButtonInput, value, isPressed); }); extendedGamepad->leftThumbstick()->setValueChangedHandler(^(GC::ControllerDirectionPad* directionPad, float xValue, float yValue) diff --git a/utilities/CodeGenerator/CodeGenerator.csproj b/utilities/CodeGenerator/CodeGenerator.csproj index 01617ecd..eb5cd5c8 100644 --- a/utilities/CodeGenerator/CodeGenerator.csproj +++ b/utilities/CodeGenerator/CodeGenerator.csproj @@ -13,7 +13,7 @@ - + diff --git a/utilities/CodeGenerator/DotnetCodeGenerator.cs b/utilities/CodeGenerator/DotnetCodeGenerator.cs index 151d9e7c..6dad9ef2 100644 --- a/utilities/CodeGenerator/DotnetCodeGenerator.cs +++ b/utilities/CodeGenerator/DotnetCodeGenerator.cs @@ -233,7 +233,9 @@ private bool GenerateServiceFunction(CppCompilation compilation, string serviceN if (structType != null) { - if (NeedCustomMarshaller(compilation, structType)) + var structGenerationInfo = ComputeStructGenerationInfo(compilation, structType); + + if (structGenerationInfo.NeedsCustomMarshaller) { customMarshallerTypes.Add(parameter.Name, structType); continue; @@ -249,11 +251,16 @@ private bool GenerateServiceFunction(CppCompilation compilation, string serviceN } } + // TODO: Refactor utf 8 marshalling with result.ApplicationPath = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(resultUnsafe.ApplicationPath); + + var returnNeedsMarshalling = false; CppType? returnTypeMarshalling = null; if (function.ReturnType.GetDisplayName() != "void") { + var structGenerationInfo = ComputeStructGenerationInfo(compilation, function.ReturnType); + if (function.ReturnType.GetDisplayName().Contains("Span")) { var returnName = function.ReturnType.GetDisplayName().Replace("Elem", string.Empty); @@ -268,7 +275,7 @@ private bool GenerateServiceFunction(CppCompilation compilation, string serviceN returnNeedsMarshalling = true; } - else if (NeedCustomMarshaller(compilation, function.ReturnType)) + else if (structGenerationInfo.NeedsCustomMarshaller) { returnNeedsMarshalling = true; returnTypeMarshalling = function.ReturnType; @@ -553,10 +560,10 @@ private void GenerateInterop(string name, string outputPath, CppCompilation comp Indent(stringBuilder); stringBuilder.AppendLine("[UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })]"); - var returnNeedsMarshalling = NeedCustomMarshaller(compilation, function.ReturnType); + var returnStructGenerationInfo = ComputeStructGenerationInfo(compilation, function.ReturnType); Indent(stringBuilder); - stringBuilder.Append($"internal static partial {MapType(compilation, function.ReturnType, forInterop: true, isReturnValue: true)}{(returnNeedsMarshalling ? "Unsafe" : "")} {functionName}("); + stringBuilder.Append($"internal static partial {MapType(compilation, function.ReturnType, forInterop: true, isReturnValue: true)}{(returnStructGenerationInfo.NeedsCustomMarshaller ? "Unsafe" : "")} {functionName}("); var firstParameter = true; @@ -584,7 +591,9 @@ private void GenerateInterop(string name, string outputPath, CppCompilation comp if (structType != null) { - if (NeedCustomMarshaller(compilation, structType)) + var structGenerationInfo = ComputeStructGenerationInfo(compilation, structType); + + if (structGenerationInfo.NeedsCustomMarshaller) { stringBuilder.Append($"{MapType(compilation, parameter.Type)}Unsafe {parameter.Name}"); continue; @@ -649,29 +658,20 @@ private void GenerateDelegate(CppCompilation compilation, CppType canonicalType, Console.WriteLine($"Generate Delegate: {canonicalType.GetDisplayName()}"); - var needMarshaller = false; var result = FunctionPointerRegex().Match(canonicalType.GetDisplayName()); if (result.Success) { - stringBuilder.Append("##MARSHALLER##"); - stringBuilder.AppendLine("[UnmanagedFunctionPointer(CallingConvention.Cdecl)]"); stringBuilder.Append($"public delegate {result.Groups[1].Value.Trim()} {typeName}("); var parameterResults = FunctionPointerParameterRegex().Matches(result.Groups[2].Value); var firstParameter = true; - var parametersDeclaration = new StringBuilder(); - var parametersValue = new StringBuilder(); - var customBeforeCode = new StringBuilder(); - var parameterChecks = new StringBuilder(); foreach (Match parameterResult in parameterResults) { if (!firstParameter) { stringBuilder.Append(", "); - parametersDeclaration.Append(", "); - parametersValue.Append(", "); } else { @@ -691,47 +691,10 @@ private void GenerateDelegate(CppCompilation compilation, CppType canonicalType, var parameterName = parameterResult.Groups[2].Value.Trim(); - if (parameterType.Contains("Span")) - { - needMarshaller = true; - - customBeforeCode.AppendLine(); - customBeforeCode.AppendLine($$""" - var {{parameterName}}Counter = 0; - var {{parameterName}}Pointer = (byte*){{parameterName}}; - - while ({{parameterName}}Pointer[{{parameterName}}Counter] != 0) - { - {{parameterName}}Counter++; - } - - {{parameterName}}Counter++; - """); - } - stringBuilder.Append($"{parameterType} {parameterName}"); - parametersDeclaration.Append($"{MapTypeToUnmanaged(parameterType)} {parameterName}"); - parametersValue.Append($"{MapValueToUnmanaged(parameterType, parameterName)}"); - parameterChecks.Append($" || {parameterName} == null"); } stringBuilder.AppendLine(");"); - - if (!needMarshaller) - { - stringBuilder.Replace("##MARSHALLER##", string.Empty); - } - else - { - stringBuilder.Replace("##MARSHALLER##", $"[NativeMarshalling(typeof({typeName}Marshaller))]\n"); - stringBuilder.AppendLine(); - stringBuilder.AppendLine(HandlerMarshallerTemplate); - stringBuilder.Replace("##TYPENAME##", typeName); - stringBuilder.Replace("##PARAMETERS_DECLARATION##", parametersDeclaration.ToString()); - stringBuilder.Replace("##PARAMETERS_VALUE##", parametersValue.ToString()); - stringBuilder.Replace("##CUSTOM_BEFORE_CODE##", customBeforeCode.ToString()); - stringBuilder.Replace("##PARAMETER_CHECKS##", parameterChecks.ToString()); - } } } @@ -838,8 +801,17 @@ private void GenerateEnum(CppType type, string typeName, StringBuilder stringBui stringBuilder.AppendLine("}"); } - private bool NeedCustomMarshaller(CppCompilation compilation, CppType type) + public readonly record struct StructGenerationInfo + { + public bool NeedsCustomMarshaller { get; init; } + public bool UseGenerics { get; init; } + } + + private StructGenerationInfo ComputeStructGenerationInfo(CppCompilation compilation, CppType type) { + bool needsCutomMarshaller = false; + bool useGenerics = false; + CppClass? structType = null; if (type is CppClass classType) @@ -852,7 +824,7 @@ private bool NeedCustomMarshaller(CppCompilation compilation, CppType type) if (qualifiedType == null) { - return false; + return new StructGenerationInfo {}; } var classTypeTemp = qualifiedType.ElementType as CppClass; @@ -865,18 +837,26 @@ private bool NeedCustomMarshaller(CppCompilation compilation, CppType type) if (structType == null) { - return false; + return new StructGenerationInfo {}; } foreach (var field in structType.Fields) { if (FieldNeedCustomMarshaller(compilation, field.Type)) { - return true; + needsCutomMarshaller = true; + } + else if(FieldIsGeneric(compilation, field.Type)) + { + useGenerics = true; } } - return false; + return new StructGenerationInfo + { + NeedsCustomMarshaller = needsCutomMarshaller, + UseGenerics = useGenerics + }; } private bool FieldNeedCustomMarshaller(CppCompilation compilation, CppType type) @@ -891,6 +871,18 @@ private bool FieldNeedCustomMarshaller(CppCompilation compilation, CppType type) return false; } + private bool FieldIsGeneric(CppCompilation compilation, CppType type) + { + var mappedType = MapType(compilation, type); + + if (mappedType.Contains("T")) + { + return true; + } + + return false; + } + private void GenerateStruct(CppCompilation compilation, CppType type, string typeName, StringBuilder stringBuilder) { var structType = type as CppClass; @@ -905,11 +897,11 @@ private void GenerateStruct(CppCompilation compilation, CppType type, string typ GenerateComment(stringBuilder, structType.Comment, 0); } - var needsCustomMarshaller = NeedCustomMarshaller(compilation, structType); + var structGenerationInfo = ComputeStructGenerationInfo(compilation, structType); - if (typeName.Contains("Options") || typeName.Contains("Parameters")) + if (structGenerationInfo.UseGenerics) { - stringBuilder.AppendLine($"public ref struct {typeName}"); + stringBuilder.AppendLine($"public ref struct {typeName} where T : unmanaged"); } else { @@ -942,7 +934,7 @@ private void GenerateStruct(CppCompilation compilation, CppType type, string typ stringBuilder.AppendLine("}"); - if (needsCustomMarshaller) + if (structGenerationInfo.NeedsCustomMarshaller) { stringBuilder.AppendLine(); stringBuilder.AppendLine($"internal unsafe struct {typeName}Unsafe"); @@ -1112,7 +1104,8 @@ private string GetSpanUnsafe(CppCompilation compilation, string value) if (cppType != null) { - needsCustomMarshalling = NeedCustomMarshaller(compilation, cppType); + var structGenerationInfo = ComputeStructGenerationInfo(compilation, cppType); + needsCustomMarshalling = structGenerationInfo.NeedsCustomMarshaller; } return $"SpanUnsafe<{value}{(needsCustomMarshalling ? "Unsafe" : "")}>"; @@ -1135,6 +1128,8 @@ string value when value.Contains("Span") && isUnsafe => $"{value.Substring(0, va string value when value.Contains("Span") && forInterop => GetSpanUnsafe(compilation, value.Substring(0, value.IndexOf("Span"))), string value when value.Contains("Span") && isReturnValue => $"ReadOnlySpan<{value.Substring(0, value.IndexOf("Span"))}>", string value when value.Contains("Span") => $"ReadOnlySpan<{value.Substring(0, value.IndexOf("Span"))}>", + string value when value == "void *" && isUnsafe => "nint", + string value when value == "void *" => "T", string value when ConstStructPointerRegex().IsMatch(value) => "in " + ConstStructPointerRegex().Match(value).Groups[1].Value, string value when value.Contains("HandlerPtr") => typeName.Replace("Ptr", string.Empty), _ => typeName From 666766ba85d360c7ba32363fe2521428ae5b1498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 12 Dec 2024 10:48:35 +0100 Subject: [PATCH 34/93] Update Project --- external/cimgui | 2 +- samples/Common/SampleModelViewerInputs.h | 36 +++++++++---------- .../Common/Inputs/HidDualSenseGamepad.cpp | 7 ++-- src/Elemental/Elemental.h | 12 +++---- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/external/cimgui b/external/cimgui index 35a4e8f8..3d5b2e76 160000 --- a/external/cimgui +++ b/external/cimgui @@ -1 +1 @@ -Subproject commit 35a4e8f8932c6395156ffacee288b9c30e50cb63 +Subproject commit 3d5b2e76503942b7d2ea3d6fb5472fe98077a21c diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleModelViewerInputs.h index 9c2f5c62..06e04fca 100644 --- a/samples/Common/SampleModelViewerInputs.h +++ b/samples/Common/SampleModelViewerInputs.h @@ -40,13 +40,13 @@ typedef struct float TouchRotateSide; float Action; - float GyroRotate; - float GyroRotateXNegative; - float GyroRotateXPositive; - float GyroRotateYNegative; - float GyroRotateYPositive; - float GyroRotateZNegative; - float GyroRotateZPositive; + float AngularVelocity; + float AngularVelocityXNegative; + float AngularVelocityXPositive; + float AngularVelocityYNegative; + float AngularVelocityYPositive; + float AngularVelocityZNegative; + float AngularVelocityZPositive; } SampleModelViewerInputActions; typedef struct @@ -94,13 +94,13 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseMiddleButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); // TODO: Be careful because it can cause side issues with the touch controls - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonX, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotate); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateYNegative); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateYPositive); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateXNegative); // TODO: It seems inverted - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateXPositive); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityZNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateZNegative); - SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadAngularVelocityZPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.GyroRotateZPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonX, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocity); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityYNegative); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityYPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityXNegative); // TODO: It seems inverted + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityXPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityZNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityZNegative); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityZPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityZPositive); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); @@ -189,11 +189,11 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe state->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; state->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } - else if (inputActions->GyroRotate) + else if (inputActions->AngularVelocity) { - state->RotationDelta.X = -(inputActions->GyroRotateXPositive - inputActions->GyroRotateXNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? - state->RotationDelta.Y = -(inputActions->GyroRotateYPositive - inputActions->GyroRotateYNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? - state->RotationDelta.Z = (inputActions->GyroRotateZPositive - inputActions->GyroRotateZNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + state->RotationDelta.X = -(inputActions->AngularVelocityXPositive - inputActions->AngularVelocityXNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? + state->RotationDelta.Y = -(inputActions->AngularVelocityYPositive - inputActions->AngularVelocityYNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? + state->RotationDelta.Z = (inputActions->AngularVelocityZPositive - inputActions->AngularVelocityZNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } else { diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp index ecbabbe0..5c33b448 100644 --- a/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp +++ b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp @@ -471,13 +471,13 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in auto calibration = hidData->CalibrationData; auto angularVelocityX = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityX, calibration.GyroPitchBias, calibration.GyroPitchMinus, calibration.GyroPitchPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); - ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityX, ElemInputId_GamepadAngularVelocityXNegative, ElemInputId_GamepadAngularVelocityXPositive); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityX, ElemInputId_AngularVelocityXNegative, ElemInputId_AngularVelocityXPositive); auto angularVelocityY = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityY, calibration.GyroYawBias, calibration.GyroYawMinus, calibration.GyroYawPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); - ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityY, ElemInputId_GamepadAngularVelocityYNegative, ElemInputId_GamepadAngularVelocityYPositive); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityY, ElemInputId_AngularVelocityYNegative, ElemInputId_AngularVelocityYPositive); auto angularVelocityZ = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityZ, calibration.GyroRollBias, calibration.GyroRollMinus, calibration.GyroRollPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); - ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityZ, ElemInputId_GamepadAngularVelocityZNegative, ElemInputId_GamepadAngularVelocityZPositive); + ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityZ, ElemInputId_AngularVelocityZNegative, ElemInputId_AngularVelocityZPositive); hidData->PreviousLeftStickX = leftStickX; hidData->PreviousLeftStickY = leftStickY; @@ -551,6 +551,7 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in .ReportId = 0x05, }; + // TODO: When this call fail we should retry next time and process the normal report auto result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&calibrationFeatureReport, sizeof(calibrationFeatureReport))); SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 799e8f68..d6903ca8 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -1361,12 +1361,12 @@ typedef enum ElemInputId_GamepadDpadDown = 141, ElemInputId_GamepadDpadLeft = 142, ElemInputId_GamepadTouchButton = 143, - ElemInputId_GamepadAngularVelocityXNegative = 144, - ElemInputId_GamepadAngularVelocityXPositive = 145, - ElemInputId_GamepadAngularVelocityYNegative = 146, - ElemInputId_GamepadAngularVelocityYPositive = 147, - ElemInputId_GamepadAngularVelocityZNegative = 148, - ElemInputId_GamepadAngularVelocityZPositive = 149, + ElemInputId_AngularVelocityXNegative = 144, + ElemInputId_AngularVelocityXPositive = 145, + ElemInputId_AngularVelocityYNegative = 146, + ElemInputId_AngularVelocityYPositive = 147, + ElemInputId_AngularVelocityZNegative = 148, + ElemInputId_AngularVelocityZPositive = 149, ElemInputId_Touch = 150, ElemInputId_TouchXNegative = 151, ElemInputId_TouchXPositive = 152, From 8d42c20541b73ad3966c91ef54ed1872dc682307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 13 Dec 2024 15:02:54 +0100 Subject: [PATCH 35/93] Refactor inputs --- samples/Common/SampleModelViewerInputs.h | 25 ++ samples/Elemental/05-HelloImGui/main.c | 2 +- src/Elemental/Common/Inputs/HidDevices.cpp | 357 ++---------------- .../Common/Inputs/HidDualSenseGamepad.cpp | 178 ++++----- .../Common/Inputs/HidSwitchGamepad.cpp | 13 + src/Elemental/Common/Inputs/HidUtils.h | 4 + .../Common/Inputs/HidXboxOneGamepad.cpp | 318 ++++++++++++++++ src/Elemental/Common/Inputs/Inputs.h | 1 + src/Elemental/Elemental.h | 22 +- 9 files changed, 468 insertions(+), 452 deletions(-) create mode 100644 src/Elemental/Common/Inputs/HidXboxOneGamepad.cpp diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleModelViewerInputs.h index 06e04fca..a886068d 100644 --- a/samples/Common/SampleModelViewerInputs.h +++ b/samples/Common/SampleModelViewerInputs.h @@ -41,12 +41,16 @@ typedef struct float Action; float AngularVelocity; + float AngularVelocityReleased; float AngularVelocityXNegative; float AngularVelocityXPositive; float AngularVelocityYNegative; float AngularVelocityYPositive; float AngularVelocityZNegative; float AngularVelocityZPositive; + + float AccelerometerZNegative; + float AccelerometerZPositive; } SampleModelViewerInputActions; typedef struct @@ -58,6 +62,7 @@ typedef struct float PreviousTouchAngle; float Zoom; float Action; + float InitialAccelerometerZDelta; } SampleModelViewerInputState; typedef struct @@ -95,12 +100,15 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) // TODO: Be careful because it can cause side issues with the touch controls SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonX, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocity); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadButtonX, 0, SampleInputActionBindingType_Released, &inputs->InputActions.AngularVelocityReleased); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityYNegative); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityYPositive); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityXNegative); // TODO: It seems inverted SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityXPositive); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityZNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityZNegative); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AngularVelocityZPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AngularVelocityZPositive); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AccelerometerZNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AccelerometerZNegative); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_AccelerometerZPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.AccelerometerZPositive); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); @@ -180,6 +188,7 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe else if (inputActions->TouchRotateSide) { SampleModelViewerInputsResetTouchParameters(state); + state->RotationDelta.Z = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } else if (inputActions->TouchReleased && !inputActions->Touch2) @@ -194,6 +203,22 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe state->RotationDelta.X = -(inputActions->AngularVelocityXPositive - inputActions->AngularVelocityXNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? state->RotationDelta.Y = -(inputActions->AngularVelocityYPositive - inputActions->AngularVelocityYNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; // TODO: Is it inverted? state->RotationDelta.Z = (inputActions->AngularVelocityZPositive - inputActions->AngularVelocityZNegative) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; + + // TODO: Use the accelerometer + /*float accelerometerZDelta = inputActions->AccelerometerZPositive - inputActions->AccelerometerZNegative; + + if (state->InitialAccelerometerZDelta) + { + float finalDelta = accelerometerZDelta - state->InitialAccelerometerZDelta; + printf("Zoom: final=%f, AccDelta=%f, Initial=%f\n", finalDelta, accelerometerZDelta, state->InitialAccelerometerZDelta); + state->Zoom += finalDelta * 2.0; + } + + state->InitialAccelerometerZDelta = accelerometerZDelta;*/ + } + else if (inputActions->AngularVelocityReleased) + { + state->InitialAccelerometerZDelta = 0.0f; } else { diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 2054359f..792ec363 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -74,7 +74,7 @@ void ImGuiInitBackend(ApplicationPayload* payload) ImFontAtlas_GetTexDataAsRGBA32(imGuiIO->Fonts, &fontPixels, &fontWidth, &fontHeight, &fontBytesPerPixel); payload->ImGuiBackendData.FontTextureId = 1; - imGuiIO->Fonts->TexID = (void*)&imGuiData->FontTextureId; + imGuiIO->Fonts->TexID = (ImTextureID)&imGuiData->FontTextureId; ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(payload->SwapChain); diff --git a/src/Elemental/Common/Inputs/HidDevices.cpp b/src/Elemental/Common/Inputs/HidDevices.cpp index f91380fa..91198c92 100644 --- a/src/Elemental/Common/Inputs/HidDevices.cpp +++ b/src/Elemental/Common/Inputs/HidDevices.cpp @@ -1,343 +1,25 @@ -#include "HidSwitchGamepad.cpp" -#include "HidDualSenseGamepad.cpp" - -// TODO: Get rid of the headers here. We should only include the cpp files #include "HidDevices.h" #include "HidUtils.h" #include "Inputs.h" -#include "SystemFunctions.h" - -enum HidGamepadVendor : uint32_t -{ - HidGamepadVendor_Microsoft = 0x045E, - HidGamepadVendor_Sony = 0x054C, - HidGamepadVendor_Nintendo = 0x057E -}; - -enum HidGamepadProduct : uint32_t -{ - HidGamepadProduct_XboxOneWirelessOldDriver = 0x02E0, - HidGamepadProduct_XboxOneUsb = 0x02FF, - HidGamepadProduct_XboxOneWireless = 0x02FD, - HidGamepadProduct_DualShock4OldDriver = 0x5C4, - HidGamepadProduct_DualShock4 = 0x9cc, - HidGamepadProduct_DualSense = 0x0ce6, - HidGamepadProduct_SwitchPro = 0x2009 -}; - -typedef void (*ProcessHidGamepadInputReportPtr)(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds); - -struct HidGamepadHandler -{ - HidGamepadVendor Vendor; - HidGamepadProduct Product; - ProcessHidGamepadInputReportPtr ProcessDataHandler; -}; - -struct __attribute__((__packed__)) XboxOneWirelessOldDriverGamepadReport -{ - uint8_t Padding; - uint16_t LeftStickX; - uint16_t LeftStickY; - uint16_t RightStickX; - uint16_t RightStickY; - uint16_t Triggers; - uint16_t Buttons; - uint8_t Dpad; -}; - -struct __attribute__((__packed__)) XboxOneWirelessGamepadReport -{ - uint8_t Padding; - uint16_t LeftStickX; - uint16_t LeftStickY; - uint16_t RightStickX; - uint16_t RightStickY; - uint16_t LeftTrigger; - uint16_t RightTrigger; - uint8_t Dpad; - uint32_t Buttons; -}; - -struct __attribute__((__packed__)) DualSenseSimpleGamepadReport -{ - uint8_t LeftStickX; - uint8_t LeftStickY; - uint16_t RightStickX; - uint16_t RightStickY; - uint16_t LeftTrigger; - uint16_t RightTrigger; - uint8_t Dpad; - uint32_t Buttons; -}; - -void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) -{ - auto xboxReport = (XboxOneWirelessGamepadReport*)hidReport.Pointer; - - // TODO: Also here we need to send the event only if the analog value has changed - float leftStickX = NormalizeInputValueSigned(xboxReport->LeftStickX, 65535, 0.2f); - - if (leftStickX <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - - if (leftStickX >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - - float leftStickY = -NormalizeInputValueSigned(xboxReport->LeftStickY, 65535, 0.2f); - - if (leftStickY <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - // TODO: check if the value is inversed!! - if (leftStickY >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(xboxReport->LeftTrigger, 1024, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadRightTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(xboxReport->RightTrigger, 1024, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - // TODO: For buttons we need to keep track of old report to see if there was a change - // Maybe we can do the same as with delta inputs! - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadButtonA, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadButtonB, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftShoulder, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x40) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadRightShoulder, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x80) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); -} - -void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) -{ - auto xboxReport = (XboxOneWirelessOldDriverGamepadReport*)hidReport.Pointer; - - // TODO: Also here we need to send the event only if the analog value has changed - float leftStickX = NormalizeInputValueSigned(xboxReport->LeftStickX, 65535, 0.2f); - - if (leftStickX <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - - if (leftStickX >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickXPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickX, - .ElapsedSeconds = elapsedSeconds - }); - } - - float leftStickY = -NormalizeInputValueSigned(xboxReport->LeftStickY, 65535, 0.2f); - - if (leftStickY <= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYNegative, - .InputType = ElemInputType_Analog, - .Value = -leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - // TODO: check if the value is inversed!! - if (leftStickY >= 0) - { - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftStickYPositive, - .InputType = ElemInputType_Analog, - .Value = leftStickY, - .ElapsedSeconds = elapsedSeconds - }); - } - - const uint16_t midpoint = 32768; - - uint8_t left, right; - // Calculate the right trigger value as a difference from midpoint - if (xboxReport->Triggers <= midpoint) { - right = static_cast((midpoint - xboxReport->Triggers) >> 7); - } else { - right = 0; - } - - // Calculate the left trigger value as a difference from midpoint - if (xboxReport->Triggers >= midpoint) { - left = static_cast((xboxReport->Triggers - midpoint) >> 7); - } else { - left = 0; - } - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(left, 255, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadRightTrigger, - .InputType = ElemInputType_Analog, - .Value = NormalizeInputValue(right, 255, 0.1f), - .ElapsedSeconds = elapsedSeconds - }); - - // TODO: For buttons we need to keep track of old report to see if there was a change - // Maybe we can do the same as with delta inputs! - - - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadButtonA, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadButtonB, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadLeftShoulder, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x10) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); - - AddInputEvent({ - .Window = window, - .InputDevice = inputDevice, - .InputId = ElemInputId_GamepadRightShoulder, - .InputType = ElemInputType_Digital, - .Value = (xboxReport->Buttons & 0x20) ? 1.0f : 0.0f, - .ElapsedSeconds = elapsedSeconds - }); -} +#include "HidDualSenseGamepad.cpp" +#include "HidXboxOneGamepad.cpp" +#include "HidSwitchGamepad.cpp" -HidGamepadHandler HidGamepadHandlers[] = +CheckHidGamepadSupportPtr HidGamepadDeviceModules[] = { - { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWirelessOldDriver, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, - { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneUsb, .ProcessDataHandler = ProcessXboxOneWirelessOldDriverGamepadReport }, - { .Vendor = HidGamepadVendor_Microsoft, .Product = HidGamepadProduct_XboxOneWireless, .ProcessDataHandler = ProcessXboxOneWirelessGamepadReport }, - { .Vendor = HidGamepadVendor_Sony, .Product = HidGamepadProduct_DualSense, .ProcessDataHandler = ProcessHidDualSenseGamepadInputReport }, - { .Vendor = HidGamepadVendor_Nintendo, .Product = HidGamepadProduct_SwitchPro, .ProcessDataHandler = ProcessHidSwitchGamepadInputReport } + CheckHidXboxOneGamepadSupport, + CheckHidSwitchProGamepadSupport, + CheckHidDualSenseGamepadSupport }; -// TODO: Allow each providers to handle registration bool IsHidDeviceSupported(uint32_t vendorId, uint32_t productId) { - for (uint32_t i = 0; i < ARRAYSIZE(HidGamepadHandlers); i++) + for (uint32_t i = 0; i < ARRAYSIZE(HidGamepadDeviceModules); i++) { - auto handler = HidGamepadHandlers[i]; + auto handler = HidGamepadDeviceModules[i](vendorId, productId); - if (vendorId == handler.Vendor && productId == handler.Product) + if (handler) { return true; } @@ -350,15 +32,20 @@ void ProcessHidDeviceData(ElemWindow window, ElemInputDevice inputDevice, ReadOn { auto inputDeviceData = GetInputDeviceData(inputDevice); SystemAssert(inputDeviceData); - - for (uint32_t i = 0; i < ARRAYSIZE(HidGamepadHandlers); i++) + + if (inputDeviceData->HidDeviceHandler == nullptr) { - auto handler = HidGamepadHandlers[i]; - - if (inputDeviceData->HidVendorId == handler.Vendor && inputDeviceData->HidProductId == handler.Product) + for (uint32_t i = 0; i < ARRAYSIZE(HidGamepadDeviceModules); i++) { - handler.ProcessDataHandler(window, inputDevice, hidReport, elapsedSeconds); - return; + inputDeviceData->HidDeviceHandler = (void*)HidGamepadDeviceModules[i](inputDeviceData->HidVendorId, inputDeviceData->HidProductId); + + if (inputDeviceData->HidDeviceHandler) + { + break; + } } } + + SystemAssert(inputDeviceData->HidDeviceHandler); + ((ProcessHidGamepadInputReportPtr)inputDeviceData->HidDeviceHandler)(window, inputDevice, hidReport, elapsedSeconds); } diff --git a/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp index 5c33b448..7c2db73e 100644 --- a/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp +++ b/src/Elemental/Common/Inputs/HidDualSenseGamepad.cpp @@ -8,17 +8,23 @@ // Based on the spec: https://controllers.fandom.com/wiki/Sony_DualSense +#define HID_DUALSENSE_VENDOR_ID 0x054C +#define HID_DUALSENSE_PRODUCT_ID 0x0CE6 + #define HID_DUALSENSE_STICK_RANGE 255 #define HID_DUALSENSE_STICK_DEAD_ZONE 0.1f #define HID_DUALSENSE_TOUCH_MAX_FINGERS 2 #define HID_DUALSENSE_TOUCH_DEAD_ZONE 0.75f +#define HID_DUALSENSE_TOUCH_SENSITIVITY 0.15f +#define HID_DUALSENSE_TOUCH_WIDTH 1920.0f +#define HID_DUALSENSE_TOUCH_HEIGHT 1070.0f #define HID_DUALSENSE_ANGULAR_VELOCITY_DEAD_ZONE 0.1f +#define HID_DUALSENSE_ACCELEROMETER_DEAD_ZONE 0.05f enum HidDualSenseGamepadInputReportType { HidDualSenseGamepadInputReportType_BluetoothStandard = 0x01, HidDualSenseGamepadInputReportType_BluetoothExtended = 0x31, - //HidDualSenseGamepadInputReportType_SubCommand = 0x21 }; enum HidDualSenseGamepadOutputReportType @@ -26,19 +32,11 @@ enum HidDualSenseGamepadOutputReportType //HidDualSenseGamepadOutputReportType_SubCommand = 0x01 }; -/* -enum HidDualSenseGamepadSubCommandType : uint8_t +enum HidDualSenseGamepadFeatureReportType { - HidDualSenseGamepadSubCommandType_DeviceInfo = 0x02 + HidDualSenseGamepadFeatureReportType_Calibration = 0x05 }; -enum HidDualSenseGamepadControllerType : uint8_t -{ - HidDualSenseGamepadControllerType_LeftJoyCon = 0x01, - HidDualSenseGamepadControllerType_RightJoyCon = 0x02, - HidDualSenseGamepadControllerType_ProController = 0x03, -};*/ - enum HidDualSenseGamepadButton : uint16_t { HidDualSenseGamepadButton_DpadUp = 1 << 0, @@ -107,31 +105,6 @@ struct __attribute__((__packed__)) HidDualSenseGamepadInputReportBluetoothStanda uint8_t RightTrigger; }; -/* -struct __attribute__((__packed__)) HidDualSenseGamepadInputReportDeviceInfo -{ - uint16_t FirmwareVersion; - HidDualSenseGamepadControllerType ControllerType; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadOutputReportSubCommand -{ - uint8_t ReportId; - uint8_t GlobalPacketNumber; - // TODO: Rumble Data - uint8_t RumbleData[8]; - HidDualSenseGamepadSubCommandType SubCommandId; - uint8_t Data[53]; -}; - -struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportSetupMemoryRead -{ - uint8_t ReportId; - uint32_t Address; - uint16_t Size; - uint8_t Checksum; -};*/ - struct __attribute__((__packed__)) HidDualSenseGamepadFeatureReportCalibration { uint8_t ReportId; @@ -178,7 +151,11 @@ struct HidDualSenseGamepadData uint16_t PreviousButtons; uint8_t PreviousMappedDpad; uint8_t PreviousSystemButtons; + float PreviousAccelerometerX; + float PreviousAccelerometerY; + float PreviousAccelerometerZ; HidDualSenseGamepadTouchState TouchState[HID_DUALSENSE_TOUCH_MAX_FINGERS]; + bool IsCalibrated; HidDualSenseGamepadFeatureReportCalibration CalibrationData; }; @@ -197,27 +174,6 @@ HidDualSenseGamepadData* GetHidDualSenseGamepadData(ElemHandle hidDevice) return SystemGetDataPoolItem(hidDualSenseGamepadDataPool, hidDevice); } -/* -bool SendHidDualSenseGamepadSubCommand(ElemInputDevice inputDevice, HidDualSenseGamepadSubCommandType subCommandType, ReadOnlySpan data) -{ - auto inputDeviceData = GetInputDeviceData(inputDevice); - SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); - - auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); - SystemAssert(hidData); - - HidDualSenseGamepadOutputReportSubCommand outputReport = - { - .ReportId = HidDualSenseGamepadOutputReportType_SubCommand, - .GlobalPacketNumber = hidData->CurrentOutputPacketNumber, - .SubCommandId = subCommandType - }; - - hidData->CurrentOutputPacketNumber = (hidData->CurrentOutputPacketNumber + 1) % 255; - - return PlatformHidSendOutputReport(inputDevice, ReadOnlySpan((uint8_t*)&outputReport, sizeof(outputReport))); -}*/ - uint8_t MapHidDualSenseGamepadDpad(uint8_t dpadData) { static const uint8_t mapping[8] = { @@ -236,9 +192,6 @@ uint8_t MapHidDualSenseGamepadDpad(uint8_t dpadData) void ParseHidDualSenseGamepadTouchData(ElemWindow window, ElemInputDevice inputDevice, double elapsedSeconds, HidDualSenseGamepadTouchState* touchState, uint8_t fingerIndex, uint32_t touchData) { - // TODO: Replace Magic numbers with constant values - - auto fingerId = (touchData >> 0) & 0x7F; auto isTouching = !((touchData >> 7) & 0x01); auto positionX = (int32_t)((touchData >> 8) & 0xFFF); auto positionY = (int32_t)((touchData >> 20) & 0xFFF); @@ -273,8 +226,8 @@ void ParseHidDualSenseGamepadTouchData(ElemWindow window, ElemInputDevice inputD } else { - deltaX = (float)(positionX - touchState->PreviousPositionX) * 0.15f; - deltaY = (float)(positionY - touchState->PreviousPositionY) * 2.0f * 0.15f; + deltaX = (float)(positionX - touchState->PreviousPositionX) * HID_DUALSENSE_TOUCH_SENSITIVITY; + deltaY = (float)(positionY - touchState->PreviousPositionY) * (HID_DUALSENSE_TOUCH_WIDTH / HID_DUALSENSE_TOUCH_HEIGHT) * HID_DUALSENSE_TOUCH_SENSITIVITY; } if (deltaX < -HID_DUALSENSE_TOUCH_DEAD_ZONE) @@ -341,10 +294,9 @@ void ParseHidDualSenseGamepadTouchData(ElemWindow window, ElemInputDevice inputD touchState->PreviousDeltaYSteps = 0; } - // TODO: Replace magic numbers if (touchState->PreviousDeltaXSteps == 0 || (isTouching != touchState->IsTouching)) { - auto normalizedPositionX = (float)positionX / 1920.0f; + auto normalizedPositionX = (float)positionX / HID_DUALSENSE_TOUCH_WIDTH; AddInputEvent({ .Window = window, @@ -359,7 +311,7 @@ void ParseHidDualSenseGamepadTouchData(ElemWindow window, ElemInputDevice inputD if (touchState->PreviousDeltaYSteps == 0 || (isTouching != touchState->IsTouching)) { - auto normalizedPositionY = (float)positionY / 1070.0f; + auto normalizedPositionY = (float)positionY / HID_DUALSENSE_TOUCH_HEIGHT; AddInputEvent({ .Window = window, @@ -408,6 +360,20 @@ float NormalizeHidDualSenseGamepadAngularVelocity(int16_t value, int16_t bias, i return normalizedValue; } +float NormalizeHidDualSenseGamepadAccelerometer(int16_t value, float previousValue, int16_t thresholdMinus, int16_t thresholdPlus) +{ + auto bias = (thresholdPlus + thresholdMinus) / 2.0f; + auto scaleFactor = (thresholdPlus - thresholdMinus) / 2.0f; + float normalizedValue = (value - bias) / scaleFactor; + + if (SystemAbs(normalizedValue - previousValue) < HID_DUALSENSE_ACCELEROMETER_DEAD_ZONE) + { + return previousValue; + } + + return normalizedValue; +} + void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReport* inputReport, double elapsedSeconds) { auto inputDeviceData = GetInputDeviceData(inputDevice); @@ -459,8 +425,7 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in // TODO: DualSense Edge buttons? - // TODO: Find another way to check if we are in advanced format - if (hidData->CalibrationData.AccelXPlus != 0) + if (hidData->IsCalibrated) { for (uint32_t i = 0; i < HID_DUALSENSE_TOUCH_MAX_FINGERS; i++) { @@ -479,6 +444,15 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in auto angularVelocityZ = NormalizeHidDualSenseGamepadAngularVelocity(inputReport->AngularVelocityZ, calibration.GyroRollBias, calibration.GyroRollMinus, calibration.GyroRollPlus, calibration.GyroSpeedMinus, calibration.GyroSpeedPlus); ProcessHidGamepadDeltaAxe(window, inputDevice, elapsedSeconds, angularVelocityZ, ElemInputId_AngularVelocityZNegative, ElemInputId_AngularVelocityZPositive); + auto accelerometerX = NormalizeHidDualSenseGamepadAccelerometer(inputReport->AccelerometerX, hidData->PreviousAccelerometerX, calibration.AccelXMinus, calibration.AccelXPlus); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, accelerometerX, hidData->PreviousAccelerometerX, ElemInputId_AccelerometerXNegative, ElemInputId_AccelerometerXPositive); + + auto accelerometerY = NormalizeHidDualSenseGamepadAccelerometer(inputReport->AccelerometerY, hidData->PreviousAccelerometerY, calibration.AccelYMinus, calibration.AccelYPlus); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, accelerometerY, hidData->PreviousAccelerometerY, ElemInputId_AccelerometerYNegative, ElemInputId_AccelerometerYPositive); + + auto accelerometerZ = NormalizeHidDualSenseGamepadAccelerometer(inputReport->AccelerometerZ, hidData->PreviousAccelerometerZ, calibration.AccelZMinus, calibration.AccelZPlus); + ProcessHidGamepadStick(window, inputDevice, elapsedSeconds, accelerometerZ, hidData->PreviousAccelerometerZ, ElemInputId_AccelerometerZNegative, ElemInputId_AccelerometerZPositive); + hidData->PreviousLeftStickX = leftStickX; hidData->PreviousLeftStickY = leftStickY; hidData->PreviousRightStickX = rightStickX; @@ -488,12 +462,15 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in hidData->PreviousButtons = inputReport->Buttons; hidData->PreviousMappedDpad = mappedDpad; hidData->PreviousSystemButtons = inputReport->SystemButtons; + hidData->PreviousAccelerometerX = accelerometerX; + hidData->PreviousAccelerometerY = accelerometerY; + hidData->PreviousAccelerometerZ = accelerometerZ; } } void ProcessHidDualSenseGamepadInputReportBluetoothStandard(ElemWindow window, ElemInputDevice inputDevice, HidDualSenseGamepadInputReportBluetoothStandard* inputReport, double elapsedSeconds) { - //SystemLogWarningMessage(ElemLogMessageCategory_Inputs, "Using DualSense GamePad Bluetooth Standard Report %f", elapsedSeconds); + SystemLogWarningMessage(ElemLogMessageCategory_Inputs, "Using DualSense GamePad Bluetooth Standard Report %f", elapsedSeconds); HidDualSenseGamepadInputReport mappedInputReport = { @@ -519,43 +496,32 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in auto inputDeviceData = GetInputDeviceData(inputDevice); SystemAssert(inputDeviceData != ELEM_HANDLE_NULL); - // TODO: This part is to get the calibration data if (inputDeviceData->HidDeviceData == ELEM_HANDLE_NULL) { - /* - // Read Config - HidDualSenseGamepadFeatureReportSetupMemoryRead featureReport = - { - .ReportId = 0x71, - .Address = 0xF8000000 + 0x6000, - .Size = 0xA000, - .Checksum = 0 - }; - - auto pointer = (uint8_t*)&featureReport; - auto sumBytes = 0u; + inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidDualSenseGamepadDataPool, {}); + } - for (uint32_t i = 0; i < sizeof(featureReport); i++) - { - sumBytes += pointer[i]; - } - - featureReport.Checksum = 0x100 - sumBytes; + auto hidDeviceData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); + SystemAssert(hidDeviceData); - auto result = PlatformHidSendFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&featureReport, sizeof(featureReport))); - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Send Feature Result: %d", result); -*/ + if (!hidDeviceData->IsCalibrated) + { HidDualSenseGamepadFeatureReportCalibration calibrationFeatureReport = { - // TODO: Replace with enum - .ReportId = 0x05, + .ReportId = HidDualSenseGamepadFeatureReportType_Calibration, }; - // TODO: When this call fail we should retry next time and process the normal report auto result = PlatformHidGetFeatureReport(inputDevice, ReadOnlySpan((uint8_t*)&calibrationFeatureReport, sizeof(calibrationFeatureReport))); - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Get Feature Result: %d", result); + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Reading DualSense Calibration Data: (Result=%d)", result); + + if (result) + { + hidDeviceData->IsCalibrated = true; + hidDeviceData->CalibrationData = calibrationFeatureReport; - inputDeviceData->HidDeviceData = SystemAddDataPoolItem(hidDualSenseGamepadDataPool, { .CalibrationData = calibrationFeatureReport }); + // TODO: Set light of current player + // TODO: Have a system to deactivate some features if not needed or to optin. (Like the gyro and accel) + } } auto reportId = hidReport[0]; @@ -570,20 +536,14 @@ void ProcessHidDualSenseGamepadInputReport(ElemWindow window, ElemInputDevice in auto inputReport = (HidDualSenseGamepadInputReport*)(hidReport.Pointer + 2); ProcessHidDualSenseGamepadInputReport(window, inputDevice, inputReport, elapsedSeconds); } - /* - else if (reportId == HidDualSenseGamepadInputReportType_SubCommand) - { - auto hidData = GetHidDualSenseGamepadData(inputDeviceData->HidDeviceData); - SystemAssert(hidData); - - - auto inputReport = (HidDualSenseGamepadInputReportExtended*)(++hidReport.Pointer); +} - uint8_t *data = inputReport->LeftStick; - uint16_t stick_horizontal = data[0] | ((data[1] & 0xF) << 8); - uint16_t stick_vertical = (data[1] >> 4) | (data[2] << 4); +ProcessHidGamepadInputReportPtr CheckHidDualSenseGamepadSupport(uint32_t vendorId, uint32_t productId) +{ + if (vendorId == HID_DUALSENSE_VENDOR_ID && productId == HID_DUALSENSE_PRODUCT_ID) + { + return ProcessHidDualSenseGamepadInputReport; + } - SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Report: Timer=%d, X=%d, Y=%d", inputReport->Timer, stick_horizontal, stick_vertical); - //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); - }*/ + return nullptr; } diff --git a/src/Elemental/Common/Inputs/HidSwitchGamepad.cpp b/src/Elemental/Common/Inputs/HidSwitchGamepad.cpp index e32b54c0..6448da14 100644 --- a/src/Elemental/Common/Inputs/HidSwitchGamepad.cpp +++ b/src/Elemental/Common/Inputs/HidSwitchGamepad.cpp @@ -8,6 +8,9 @@ // Based on the spec: https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/bluetooth_hid_notes.md +#define HID_SWITCH_VENDOR_ID 0x057E +#define HID_SWITCH_PRODUCT_ID 0x2009 + #define HID_SWITCH_STICK_RANGE 65535 #define HID_SWITCH_STICK_DEAD_ZONE 0.2f @@ -255,3 +258,13 @@ inline void ProcessHidSwitchGamepadInputReport(ElemWindow window, ElemInputDevic //SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "ProcessSubCommandtype: %d, Version: %d, Type: %d", subCommandType, inputReport->FirmwareVersion, inputReport->ControllerType); } } + +ProcessHidGamepadInputReportPtr CheckHidSwitchProGamepadSupport(uint32_t vendorId, uint32_t productId) +{ + if (vendorId == HID_SWITCH_VENDOR_ID && productId == HID_SWITCH_PRODUCT_ID) + { + return ProcessHidSwitchGamepadInputReport; + } + + return nullptr; +} diff --git a/src/Elemental/Common/Inputs/HidUtils.h b/src/Elemental/Common/Inputs/HidUtils.h index 3f58a80c..80240b59 100644 --- a/src/Elemental/Common/Inputs/HidUtils.h +++ b/src/Elemental/Common/Inputs/HidUtils.h @@ -3,7 +3,11 @@ #include "../Elemental.h" #include "Inputs.h" +typedef void (*ProcessHidGamepadInputReportPtr)(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds); +typedef ProcessHidGamepadInputReportPtr (*CheckHidGamepadSupportPtr)(uint32_t vendorId, uint32_t productId); + // TODO: Create a CPP file +// TODO: Rename that file inline float NormalizeInputValue(uint32_t value, uint32_t maxValue, float deadZone) { diff --git a/src/Elemental/Common/Inputs/HidXboxOneGamepad.cpp b/src/Elemental/Common/Inputs/HidXboxOneGamepad.cpp new file mode 100644 index 00000000..a8c5d012 --- /dev/null +++ b/src/Elemental/Common/Inputs/HidXboxOneGamepad.cpp @@ -0,0 +1,318 @@ +#pragma once + +#include "../Elemental.h" +#include "Inputs.h" +#include "HidUtils.h" +#include "SystemFunctions.h" +#include "SystemDataPool.h" + +#define HID_XBOXONE_VENDOR_ID 0x045E +#define HID_XBOXONE_PRODUCT_ID 0x02FD +#define HID_XBOXONE_OLD_DRIVER_PRODUCT_ID 0x02E0 +#define HID_XBOXONE_USB_PRODUCT_ID 0x02FF + +struct __attribute__((__packed__)) XboxOneWirelessOldDriverGamepadReport +{ + uint8_t Padding; + uint16_t LeftStickX; + uint16_t LeftStickY; + uint16_t RightStickX; + uint16_t RightStickY; + uint16_t Triggers; + uint16_t Buttons; + uint8_t Dpad; +}; + +struct __attribute__((__packed__)) XboxOneWirelessGamepadReport +{ + uint8_t Padding; + uint16_t LeftStickX; + uint16_t LeftStickY; + uint16_t RightStickX; + uint16_t RightStickY; + uint16_t LeftTrigger; + uint16_t RightTrigger; + uint8_t Dpad; + uint32_t Buttons; +}; + +struct __attribute__((__packed__)) DualSenseSimpleGamepadReport +{ + uint8_t LeftStickX; + uint8_t LeftStickY; + uint16_t RightStickX; + uint16_t RightStickY; + uint16_t LeftTrigger; + uint16_t RightTrigger; + uint8_t Dpad; + uint32_t Buttons; +}; + +void ProcessXboxOneWirelessGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + auto xboxReport = (XboxOneWirelessGamepadReport*)hidReport.Pointer; + + // TODO: Also here we need to send the event only if the analog value has changed + float leftStickX = NormalizeInputValueSigned(xboxReport->LeftStickX, 65535, 0.2f); + + if (leftStickX <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (leftStickX >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + float leftStickY = -NormalizeInputValueSigned(xboxReport->LeftStickY, 65535, 0.2f); + + if (leftStickY <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + // TODO: check if the value is inversed!! + if (leftStickY >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(xboxReport->LeftTrigger, 1024, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadRightTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(xboxReport->RightTrigger, 1024, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + // TODO: For buttons we need to keep track of old report to see if there was a change + // Maybe we can do the same as with delta inputs! + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadButtonA, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadButtonB, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftShoulder, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x40) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadRightShoulder, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x80) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); +} + +void ProcessXboxOneWirelessOldDriverGamepadReport(ElemWindow window, ElemInputDevice inputDevice, ReadOnlySpan hidReport, double elapsedSeconds) +{ + auto xboxReport = (XboxOneWirelessOldDriverGamepadReport*)hidReport.Pointer; + + // TODO: Also here we need to send the event only if the analog value has changed + float leftStickX = NormalizeInputValueSigned(xboxReport->LeftStickX, 65535, 0.2f); + + if (leftStickX <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + if (leftStickX >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickXPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickX, + .ElapsedSeconds = elapsedSeconds + }); + } + + float leftStickY = -NormalizeInputValueSigned(xboxReport->LeftStickY, 65535, 0.2f); + + if (leftStickY <= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYNegative, + .InputType = ElemInputType_Analog, + .Value = -leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + // TODO: check if the value is inversed!! + if (leftStickY >= 0) + { + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftStickYPositive, + .InputType = ElemInputType_Analog, + .Value = leftStickY, + .ElapsedSeconds = elapsedSeconds + }); + } + + const uint16_t midpoint = 32768; + + uint8_t left, right; + + // Calculate the right trigger value as a difference from midpoint + if (xboxReport->Triggers <= midpoint) { + right = static_cast((midpoint - xboxReport->Triggers) >> 7); + } else { + right = 0; + } + + // Calculate the left trigger value as a difference from midpoint + if (xboxReport->Triggers >= midpoint) { + left = static_cast((xboxReport->Triggers - midpoint) >> 7); + } else { + left = 0; + } + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(left, 255, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadRightTrigger, + .InputType = ElemInputType_Analog, + .Value = NormalizeInputValue(right, 255, 0.1f), + .ElapsedSeconds = elapsedSeconds + }); + + // TODO: For buttons we need to keep track of old report to see if there was a change + // Maybe we can do the same as with delta inputs! + + + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadButtonA, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x01) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadButtonB, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x02) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadLeftShoulder, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x10) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); + + AddInputEvent({ + .Window = window, + .InputDevice = inputDevice, + .InputId = ElemInputId_GamepadRightShoulder, + .InputType = ElemInputType_Digital, + .Value = (xboxReport->Buttons & 0x20) ? 1.0f : 0.0f, + .ElapsedSeconds = elapsedSeconds + }); +} + + +ProcessHidGamepadInputReportPtr CheckHidXboxOneGamepadSupport(uint32_t vendorId, uint32_t productId) +{ + if (vendorId == HID_XBOXONE_VENDOR_ID) + { + if (productId == HID_XBOXONE_OLD_DRIVER_PRODUCT_ID || productId == HID_XBOXONE_USB_PRODUCT_ID) + { + return ProcessXboxOneWirelessOldDriverGamepadReport; + } + else if (productId == HID_XBOXONE_PRODUCT_ID) + { + return ProcessXboxOneWirelessGamepadReport; + } + } + + return nullptr; +} + diff --git a/src/Elemental/Common/Inputs/Inputs.h b/src/Elemental/Common/Inputs/Inputs.h index 4b69643f..996922f1 100644 --- a/src/Elemental/Common/Inputs/Inputs.h +++ b/src/Elemental/Common/Inputs/Inputs.h @@ -14,6 +14,7 @@ struct InputDeviceData uint32_t HidProductId; void* PlatformData; ElemHandle HidDeviceData; + void* HidDeviceHandler; }; struct InputDeviceDataFull diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index d6903ca8..2eab1b0a 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -1023,6 +1023,8 @@ ElemAPI ElemFence ElemExecuteCommandList(ElemCommandQueue commandQueue, ElemComm */ ElemAPI ElemFence ElemExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandListSpan commandLists, const ElemExecuteCommandListOptions* options); +// TODO: Add a create fence function for a given commanduque? + // TODO: Still in draft! ElemAPI ElemIOCommandQueue ElemCreateIOCommandQueue(const ElemIOCommandQueueOptions* options); ElemAPI void ElemFreeIOCommandQueue(ElemIOCommandQueue ioQueue); @@ -1367,13 +1369,19 @@ typedef enum ElemInputId_AngularVelocityYPositive = 147, ElemInputId_AngularVelocityZNegative = 148, ElemInputId_AngularVelocityZPositive = 149, - ElemInputId_Touch = 150, - ElemInputId_TouchXNegative = 151, - ElemInputId_TouchXPositive = 152, - ElemInputId_TouchYNegative = 153, - ElemInputId_TouchYPositive = 154, - ElemInputId_TouchXAbsolutePosition = 155, - ElemInputId_TouchYAbsolutePosition = 156 + ElemInputId_AccelerometerXNegative = 150, + ElemInputId_AccelerometerXPositive = 151, + ElemInputId_AccelerometerYNegative = 152, + ElemInputId_AccelerometerYPositive = 153, + ElemInputId_AccelerometerZNegative = 154, + ElemInputId_AccelerometerZPositive = 155, + ElemInputId_Touch = 156, + ElemInputId_TouchXNegative = 157, + ElemInputId_TouchXPositive = 158, + ElemInputId_TouchYNegative = 159, + ElemInputId_TouchYPositive = 160, + ElemInputId_TouchXAbsolutePosition = 161, + ElemInputId_TouchYAbsolutePosition = 162 } ElemInputId; typedef struct From 5a5669881e27588d18b6b8a690342a3b45747021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 16 Dec 2024 11:31:24 +0100 Subject: [PATCH 36/93] Refactor tools to use path --- .gitmodules | 3 + external/CMakeLists.txt | 9 +- external/fast_obj | 1 + .../ElementalTools/01-ShaderCompiler/main.c | 11 +- samples/ElementalTools/02-MeshCompiler/main.c | 12 ++ src/Elemental/Common/Graphics/ShaderReader.h | 8 +- src/Elemental/Common/SystemFunctions.cpp | 2 +- .../Microsoft/SystemPlatformFunctions.cpp | 6 +- src/ElementalTools/Common/MeshLoader.cpp | 64 ++++++ src/ElementalTools/Common/MeshletBuilder.cpp | 5 +- src/ElementalTools/Common/ShaderCompiler.cpp | 51 ++++- .../Common/ShaderCompilerUtils.h | 8 +- src/ElementalTools/Common/ToolsUtils.cpp | 47 +++++ src/ElementalTools/Common/ToolsUtils.h | 6 + src/ElementalTools/Common/UnityBuild.cpp | 5 + src/ElementalTools/ElementalTools.h | 84 ++++++-- src/ElementalTools/ElementalToolsLoader.c | 60 +++++- src/ElementalTools/Linux/PreCompiledHeader.h | 1 + .../Microsoft/PreCompiledHeader.h | 1 + tests/ToolsTests/MeshLoaderTests.cpp | 74 +++++++ tests/ToolsTests/ShaderCompilerTests.cpp | 76 ++----- tests/ToolsTests/ToolsTests.cpp | 53 +++++ tests/ToolsTests/ToolsTests.h | 4 +- tests/ToolsTests/UnityBuild.cpp | 11 +- utilities/cmake/AppPackaging.cmake | 189 +++++++++++++----- 25 files changed, 622 insertions(+), 169 deletions(-) create mode 160000 external/fast_obj create mode 100644 src/ElementalTools/Common/MeshLoader.cpp create mode 100644 src/ElementalTools/Common/ToolsUtils.cpp create mode 100644 src/ElementalTools/Common/ToolsUtils.h create mode 100644 tests/ToolsTests/MeshLoaderTests.cpp create mode 100644 tests/ToolsTests/ToolsTests.cpp diff --git a/.gitmodules b/.gitmodules index 403dd5da..547364f6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "external/cimgui"] path = external/cimgui url = https://github.com/cimgui/cimgui.git +[submodule "external/fast_obj"] + path = external/fast_obj + url = https://github.com/thisistherk/fast_obj.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 6f8ec215..86a0b040 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -210,11 +210,18 @@ if(NOT BUILD_FOR_IOS) add_subdirectory(./meshoptimizer) endif() +#======================================================================= +# Fast Obj +#======================================================================= +if(NOT BUILD_FOR_IOS) + add_subdirectory(./fast_obj) +endif() + #======================================================================= # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj) #======================================================================= # xxHash diff --git a/external/fast_obj b/external/fast_obj new file mode 160000 index 00000000..9884aa86 --- /dev/null +++ b/external/fast_obj @@ -0,0 +1 @@ +Subproject commit 9884aa86b00a74122719754006b63e6dfcba238f diff --git a/samples/ElementalTools/01-ShaderCompiler/main.c b/samples/ElementalTools/01-ShaderCompiler/main.c index 8becb299..ee5e6261 100644 --- a/samples/ElementalTools/01-ShaderCompiler/main.c +++ b/samples/ElementalTools/01-ShaderCompiler/main.c @@ -70,16 +70,7 @@ int main(int argc, const char* argv[]) printf("Compiling shader: %s (DebugMode=%d)\n", inputPath, debugMode); - ElemToolsDataSpan shaderSource = SampleReadFile(inputPath); - - if (shaderSource.Length == 0) - { - printf("File doesn't exist.\n"); - return 1; - } - - ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = shaderSource }; - ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary(targetApi, targetPlatform, &shaderSourceData, &(ElemCompileShaderOptions) { .DebugMode = debugMode }); + ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary(targetApi, targetPlatform, inputPath, &(ElemCompileShaderOptions) { .DebugMode = debugMode }); for (uint32_t i = 0; i < compilationResult.Messages.Length; i++) { diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index 2263d11b..ee4077dd 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -201,6 +201,7 @@ InputMeshData ReadObjMesh(ElemToolsDataSpan data, bool reverseHandedness) if (SampleCompareString(lineParts[0], "g")) { printf("ERROR: Group not implemented yet!\n"); + return (InputMeshData) { 0 }; } else if (SampleCompareString(lineParts[0], "v")) { @@ -302,9 +303,20 @@ int main(int argc, const char* argv[]) return 1; } + ElemLoadMeshResult inputMesh = ElemLoadMesh(inputData, ElemMeshFormat_Obj, NULL); + printf("Input mesh vertex Count: %d\n", inputMesh.VertexCount); + InputMeshData inputMeshData = ReadObjMesh(inputData, true); printf("Input mesh vertex Count: %d\n", inputMeshData.VertexCount); + // TODO: Temporary + if (inputMeshData.VertexCount == 0) + { + uint8_t test = { 1 }; + SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = &test, .Length = 1 }, false); + return 0; + } + ElemVertexBuffer vertexBuffer = { .Data = { .Items = (uint8_t*)inputMeshData.VertexList, .Length = inputMeshData.VertexCount * sizeof(InputMeshVertex) }, diff --git a/src/Elemental/Common/Graphics/ShaderReader.h b/src/Elemental/Common/Graphics/ShaderReader.h index 29bacf6c..c9860b90 100644 --- a/src/Elemental/Common/Graphics/ShaderReader.h +++ b/src/Elemental/Common/Graphics/ShaderReader.h @@ -5,10 +5,10 @@ enum ShaderType { ShaderType_Unknown = 0, - ShaderType_Mesh = 2, - ShaderType_Pixel = 3, - ShaderType_Compute = 4, - ShaderType_Library = 5 + ShaderType_Mesh = 1, + ShaderType_Pixel = 2, + ShaderType_Compute = 3, + ShaderType_Library = 4 }; enum ShaderMetadataType diff --git a/src/Elemental/Common/SystemFunctions.cpp b/src/Elemental/Common/SystemFunctions.cpp index 4e4e2eea..3494572f 100644 --- a/src/Elemental/Common/SystemFunctions.cpp +++ b/src/Elemental/Common/SystemFunctions.cpp @@ -482,7 +482,7 @@ Span SystemFileReadBytes(MemoryArena memoryArena, ReadOnlySpan pa auto fileData = SystemPushArray(memoryArena, fileSizeInBytes); SystemPlatformFileReadBytes(path, fileData); - + return fileData; } diff --git a/src/Elemental/Microsoft/SystemPlatformFunctions.cpp b/src/Elemental/Microsoft/SystemPlatformFunctions.cpp index 3e635275..9fdef14b 100644 --- a/src/Elemental/Microsoft/SystemPlatformFunctions.cpp +++ b/src/Elemental/Microsoft/SystemPlatformFunctions.cpp @@ -122,7 +122,7 @@ size_t SystemPlatformFileGetSizeInBytes(ReadOnlySpan path) auto stackMemoryArena = SystemGetStackMemoryArena(); auto pathWide = SystemConvertUtf8ToWideChar(stackMemoryArena, path); - auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { @@ -141,7 +141,7 @@ void SystemPlatformFileWriteBytes(ReadOnlySpan path, ReadOnlySpan auto stackMemoryArena = SystemGetStackMemoryArena(); auto pathWide = SystemConvertUtf8ToWideChar(stackMemoryArena, path); - auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_WRITE, FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { @@ -163,7 +163,7 @@ void SystemPlatformFileReadBytes(ReadOnlySpan path, Span data) auto stackMemoryArena = SystemGetStackMemoryArena(); auto pathWide = SystemConvertUtf8ToWideChar(stackMemoryArena, path); - auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + auto fileHandle = CreateFile(pathWide.Pointer, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, nullptr); if (fileHandle == INVALID_HANDLE_VALUE) { diff --git a/src/ElementalTools/Common/MeshLoader.cpp b/src/ElementalTools/Common/MeshLoader.cpp new file mode 100644 index 00000000..80d53182 --- /dev/null +++ b/src/ElementalTools/Common/MeshLoader.cpp @@ -0,0 +1,64 @@ + +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + + +// TODO: We need to track the offset + +void* FastObjFileOpen(const char* path, void* userData) +{ + if (SystemFindSubString(path, "ObjData") != -1) + { + return nullptr; + } + + return (void*)1; +} + +void FastObjFileClose(void* file, void* userData) +{ +} + +size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userData) +{ + // TODO: Check File ID + auto sourceDataSpan = (ElemToolsDataSpan*)userData; + auto sourceSpan = ReadOnlySpan(sourceDataSpan->Items, sourceDataSpan->Length); + auto destinationSpan = Span((uint8_t*)destination, bytes); + + printf("ReadObj: %d/%d\n", bytes, sourceDataSpan->Length); + SystemCopyBuffer(destinationSpan, sourceSpan); + + return 0; +} + +unsigned long FastObjFileSize(void* file, void* userData) +{ + // TODO: Check File ID + auto dataSpan = (ElemToolsDataSpan*)userData; + return dataSpan->Length; +} + +ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, const ElemLoadMeshOptions* options) +{ + // TODO: To output the results, we should use a separate memory arena for each modules + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto hasErrors = false; + + auto callbacks = fastObjCallbacks + { + .file_open = FastObjFileOpen, + .file_close = FastObjFileClose, + .file_read = FastObjFileRead, + .file_size = FastObjFileSize + }; + + auto mesh = fast_obj_read_with_callbacks("ObjFile", &callbacks, (void*)&data); + + return + { + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp index e15eed26..6a766842 100644 --- a/src/ElementalTools/Common/MeshletBuilder.cpp +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -11,7 +11,6 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto vertexRemap = SystemPushArrayZero(stackMemoryArena, indexCount); auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, nullptr, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); - printf("VertexCount after remap: %zu\n", vertexCount); auto vertexList = SystemPushArrayZero(stackMemoryArena, vertexCount * vertexBuffer.VertexSize); auto indexList = SystemPushArrayZero(stackMemoryArena, indexCount); @@ -31,7 +30,6 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto coneWeight = 0.5f; auto meshletCount = (uint32_t)meshopt_buildMeshletsBound(indexCount, meshletMaxVertexCount, meshletMaxTriangleCount); - printf("MeshletCount: %d\n", meshletCount); auto meshOptMeshletList = SystemPushArray(stackMemoryArena, meshletCount); auto meshletVertexIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxVertexCount); @@ -54,7 +52,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto meshletVertexIndexCount = 0u; auto meshletTriangleIndexCount = 0u; - printf("MeshletCount: %d\n", meshletCount); + //printf("MeshletCount: %d\n", meshletCount); auto meshletList = SystemPushArray(stackMemoryArena, meshletCount); @@ -99,6 +97,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf meshletTriangleIndexCount = fmax(meshletTriangleIndexCount, meshlet.triangle_offset / 3 + meshlet.triangle_count); } + // TODO: Output everything in separate memory arena return { .MeshletMaxVertexCount = meshletMaxVertexCount, diff --git a/src/ElementalTools/Common/ShaderCompiler.cpp b/src/ElementalTools/Common/ShaderCompiler.cpp index 38e8ee71..1deae285 100644 --- a/src/ElementalTools/Common/ShaderCompiler.cpp +++ b/src/ElementalTools/Common/ShaderCompiler.cpp @@ -1,4 +1,5 @@ #include "ElementalTools.h" +#include "ToolsUtils.h" #include "DirectXShaderCompiler.h" #include "MetalShaderConverter.h" #include "SystemFunctions.h" @@ -83,6 +84,25 @@ ElemShaderLanguage GetApiTargetLanguage(ElemToolsGraphicsApi graphicsApi) } } +ElemShaderLanguage GetShaderLanguageFromPath(const char* path) +{ + auto pathSpan = ReadOnlySpan(path); + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto lastIndex = SystemLastIndexOf(path, '.'); + SystemAssert(lastIndex != -1); + + auto extension = SystemPushArray(stackMemoryArena, pathSpan.Length - lastIndex); + SystemCopyBuffer(extension, pathSpan.Slice(lastIndex + 1)); + + if (SystemFindSubString(extension, "hlsl") != -1) + { + return ElemShaderLanguage_Hlsl; + } + + return ElemShaderLanguage_Unknown; +} + void ReverseCompilerChainArray(Span steps, uint32_t count) { for (uint32_t i = 0; i < count - 1; i++) @@ -163,9 +183,9 @@ ElemToolsAPI bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemTo return FindShaderCompilerChain(shaderLanguage, targetLanguage, compilerSteps, &level); } -ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const ElemShaderSourceData* sourceData, const ElemCompileShaderOptions* options) +ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const char* path, const ElemCompileShaderOptions* options) { - SystemAssert(sourceData); + SystemAssert(path); InitShaderCompiler(); auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -176,7 +196,10 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph auto compilationMessages = Span(); auto compilationData = ReadOnlySpan(); - if (!FindShaderCompilerChain(sourceData->ShaderLanguage, targetLanguage, compilerSteps, &level)) + auto shaderLanguage = GetShaderLanguageFromPath(path); + SystemAssert(shaderLanguage != ElemShaderLanguage_Unknown); + + if (!FindShaderCompilerChain(shaderLanguage, targetLanguage, compilerSteps, &level)) { auto messageItem = SystemPushStruct(stackMemoryArena); messageItem->Type = ElemToolsMessageType_Error; @@ -195,7 +218,24 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph auto hasErrors = false; - auto stepSourceData = ReadOnlySpan((uint8_t*)sourceData->Data.Items, sourceData->Data.Length); + auto stepSourceData = LoadFileData(path); + + if (stepSourceData.Length == 0) + { + auto messageItem = SystemPushStruct(stackMemoryArena); + messageItem->Type = ElemToolsMessageType_Error; + messageItem->Message = "Cannot read input file."; + + return + { + .Messages = + { + .Items = messageItem, + .Length = 1 + }, + .HasErrors = true + }; + } for (uint32_t i = 0; i < level; i++) { @@ -227,6 +267,9 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph stepSourceData = compilationData; } + ResetLoadFileDataMemory(); + + // TODO: output the values in a separate arena return { .Data = diff --git a/src/ElementalTools/Common/ShaderCompilerUtils.h b/src/ElementalTools/Common/ShaderCompilerUtils.h index c66c9f5f..a282e2e8 100644 --- a/src/ElementalTools/Common/ShaderCompilerUtils.h +++ b/src/ElementalTools/Common/ShaderCompilerUtils.h @@ -5,10 +5,10 @@ enum ShaderType { ShaderType_Unknown = 0, - ShaderType_Mesh = 2, - ShaderType_Pixel = 3, - ShaderType_Compute = 4, - ShaderType_Library = 5 + ShaderType_Mesh = 1, + ShaderType_Pixel = 2, + ShaderType_Compute = 3, + ShaderType_Library = 4 }; enum ShaderMetadataType diff --git a/src/ElementalTools/Common/ToolsUtils.cpp b/src/ElementalTools/Common/ToolsUtils.cpp new file mode 100644 index 00000000..af4af472 --- /dev/null +++ b/src/ElementalTools/Common/ToolsUtils.cpp @@ -0,0 +1,47 @@ +#include "ToolsUtils.h" +#include "ElementalTools.h" +#include "SystemFunctions.h" +#include "SystemMemory.h" + +// TODO: Do one for each threads +static MemoryArena FileIOMemoryArena; + +ElemToolsDataSpan DefaultFileHandler(const char* path) +{ + if (SystemFileExists(path)) + { + auto data = SystemFileReadBytes(FileIOMemoryArena, path); + return { .Items = data.Pointer, .Length = (uint32_t)data.Length }; + } + + return {}; +} + +static ElemToolsLoadFileHandlerPtr loadFileHandlerPtr = DefaultFileHandler; + +ElemToolsAPI void ElemToolsConfigureFileIO(ElemToolsLoadFileHandlerPtr loadFileHandler) +{ + SystemAssert(loadFileHandler); + loadFileHandlerPtr = loadFileHandler; +} + +void InitStorageMemoryArena() +{ + if (FileIOMemoryArena.Storage == nullptr) + { + FileIOMemoryArena = SystemAllocateMemoryArena(); + } +} + +ReadOnlySpan LoadFileData(const char* path) +{ + InitStorageMemoryArena(); + + auto data = loadFileHandlerPtr(path); + return ReadOnlySpan(data.Items, data.Length); +} + +void ResetLoadFileDataMemory() +{ + SystemClearMemoryArena(FileIOMemoryArena); +} diff --git a/src/ElementalTools/Common/ToolsUtils.h b/src/ElementalTools/Common/ToolsUtils.h new file mode 100644 index 00000000..769dfe84 --- /dev/null +++ b/src/ElementalTools/Common/ToolsUtils.h @@ -0,0 +1,6 @@ +#pragma once + +#include "SystemSpan.h" + +ReadOnlySpan LoadFileData(const char* path); +void ResetLoadFileDataMemory(); diff --git a/src/ElementalTools/Common/UnityBuild.cpp b/src/ElementalTools/Common/UnityBuild.cpp index 612d5014..52b53d60 100644 --- a/src/ElementalTools/Common/UnityBuild.cpp +++ b/src/ElementalTools/Common/UnityBuild.cpp @@ -1,3 +1,4 @@ +#include "ToolsUtils.cpp" #include "ShaderCompiler.cpp" #include "ShaderCompilerUtils.cpp" #include "DirectXShaderCompiler.cpp" @@ -6,6 +7,10 @@ #include "MetalShaderConverter.cpp" #endif +#define FAST_OBJ_IMPLEMENTATION +#include "fast_obj.h" + +#include "MeshLoader.cpp" #include "MeshletBuilder.cpp" #include "SystemFunctions.cpp" diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 93495d9e..388fbfa9 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -40,27 +40,6 @@ // ##Module_Tools## //------------------------------------------------------------------------ -/** - * Enumerates supported shader languages. - */ -typedef enum -{ - // Unknown shader language. - ElemShaderLanguage_Unknown = 0, - // High Level Shading Language used by DirectX. - ElemShaderLanguage_Hlsl = 1, - // OpenGL Shading Language. - ElemShaderLanguage_Glsl = 2, - // Metal Shading Language for Apple devices. - ElemShaderLanguage_Msl = 3, - // DirectX Intermediate Language. - ElemShaderLanguage_Dxil = 4, - // Standard Portable Intermediate Representation. - ElemShaderLanguage_Spirv = 5, - // Intermediate representation for Metal. - ElemShaderLanguage_MetalIR = 6 -} ElemShaderLanguage; - /** * Enumerates supported graphics APIs. */ @@ -135,6 +114,37 @@ typedef struct uint32_t Length; } ElemToolsMessageSpan; +typedef ElemToolsDataSpan (*ElemToolsLoadFileHandlerPtr)(const char* path); + +ElemToolsAPI void ElemToolsConfigureFileIO(ElemToolsLoadFileHandlerPtr loadFileHandler); + + +//------------------------------------------------------------------------ +// ##Module_Shaders## +//------------------------------------------------------------------------ + +// TODO: Change the list +/** + * Enumerates supported shader languages. + */ +typedef enum +{ + // Unknown shader language. + ElemShaderLanguage_Unknown = 0, + // High Level Shading Language used by DirectX. + ElemShaderLanguage_Hlsl = 1, + // OpenGL Shading Language. + ElemShaderLanguage_Glsl = 2, + // Metal Shading Language for Apple devices. + ElemShaderLanguage_Msl = 3, + // DirectX Intermediate Language. + ElemShaderLanguage_Dxil = 4, + // Standard Portable Intermediate Representation. + ElemShaderLanguage_Spirv = 5, + // Intermediate representation for Metal. + ElemShaderLanguage_MetalIR = 6 +} ElemShaderLanguage; + /** * Represents shader source data, including language and the actual code. */ @@ -153,6 +163,8 @@ typedef struct { // If true, compile shaders in debug mode to provide more information. bool DebugMode; + + // TODO: Add the ability to add an override for shader language } ElemCompileShaderOptions; /** @@ -186,16 +198,31 @@ ElemToolsAPI bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemTo * @return The result of the compilation, including any binaries and messages. */ // TODO: Put graphics api and platform into options (default to current one) -ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const ElemShaderSourceData* sourceData, const ElemCompileShaderOptions* options); +ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, const char* path, const ElemCompileShaderOptions* options); // TODO: Can we compile multiple source files into one library? + +//------------------------------------------------------------------------ +// ##Module_Meshes## +//------------------------------------------------------------------------ + +typedef enum +{ + ElemMeshFormat_Obj = 0, +} ElemMeshFormat; + typedef struct { ElemToolsDataSpan Data; uint32_t VertexSize; } ElemVertexBuffer; +typedef struct +{ + uint32_t Reserved; +} ElemLoadMeshOptions; + // TODO: Allow to specify the offset of the vertex position? (For now we assume vertex position is at offset 0) typedef struct { @@ -222,6 +249,14 @@ typedef struct uint32_t Length; } ElemUInt32Span; +typedef struct +{ + ElemVertexBuffer VertexBuffer; + uint32_t VertexCount; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemLoadMeshResult; + typedef struct { uint8_t MeshletMaxVertexCount; @@ -234,6 +269,11 @@ typedef struct bool HasErrors; } ElemBuildMeshletResult; +// TODO: Add a way to pass multiple files? +// TODO: We need to have also a callback system for providing data based on files. Because for shaders and meshes you don't know +// in advance what files are used. The model of fast_obj is actually pretty good +ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, const ElemLoadMeshOptions* options); + ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); // TODO: Do an optimize mesh function that can be used to optimize a normal mesh (for Raytracing for example) diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index 53dc3113..a8dd59ab 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -23,8 +23,10 @@ static int functionPointersLoadedElementalTools = 0; typedef struct ElementalToolsFunctions { + void (*ElemToolsConfigureFileIO)(ElemToolsLoadFileHandlerPtr); bool (*ElemCanCompileShader)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform); - ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, ElemShaderSourceData const *, ElemCompileShaderOptions const *); + ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *); + ElemLoadMeshResult (*ElemLoadMesh)(ElemToolsDataSpan, ElemMeshFormat, ElemLoadMeshOptions const *); ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemBuildMeshletsOptions const *); } ElementalToolsFunctions; @@ -78,8 +80,10 @@ static bool LoadElementalToolsFunctionPointers(void) return functionPointersLoadedElementalTools; } + listElementalToolsFunctions.ElemToolsConfigureFileIO = (void (*)(ElemToolsLoadFileHandlerPtr))GetElementalToolsFunctionPointer("ElemToolsConfigureFileIO"); listElementalToolsFunctions.ElemCanCompileShader = (bool (*)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform))GetElementalToolsFunctionPointer("ElemCanCompileShader"); - listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, ElemShaderSourceData const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); + listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); + listElementalToolsFunctions.ElemLoadMesh = (ElemLoadMeshResult (*)(ElemToolsDataSpan, ElemMeshFormat, ElemLoadMeshOptions const *))GetElementalToolsFunctionPointer("ElemLoadMesh"); listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); @@ -87,6 +91,23 @@ static bool LoadElementalToolsFunctionPointers(void) return true; } +static inline void ElemToolsConfigureFileIO(ElemToolsLoadFileHandlerPtr loadFileHandler) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + return; + } + + if (!listElementalToolsFunctions.ElemToolsConfigureFileIO) + { + assert(listElementalToolsFunctions.ElemToolsConfigureFileIO); + return; + } + + listElementalToolsFunctions.ElemToolsConfigureFileIO(loadFileHandler); +} + static inline bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform) { if (!LoadElementalToolsFunctionPointers()) @@ -118,7 +139,7 @@ static inline bool ElemCanCompileShader(ElemShaderLanguage shaderLanguage, ElemT return listElementalToolsFunctions.ElemCanCompileShader(shaderLanguage, graphicsApi, platform); } -static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, ElemShaderSourceData const * sourceData, ElemCompileShaderOptions const * options) +static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraphicsApi graphicsApi, ElemToolsPlatform platform, char const * path, ElemCompileShaderOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { @@ -146,7 +167,38 @@ static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGrap return result; } - return listElementalToolsFunctions.ElemCompileShaderLibrary(graphicsApi, platform, sourceData, options); + return listElementalToolsFunctions.ElemCompileShaderLibrary(graphicsApi, platform, path, options); +} + +static inline ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, ElemLoadMeshOptions const * options) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + + #ifdef __cplusplus + ElemLoadMeshResult result = {}; + #else + ElemLoadMeshResult result = (ElemLoadMeshResult){0}; + #endif + + return result; + } + + if (!listElementalToolsFunctions.ElemLoadMesh) + { + assert(listElementalToolsFunctions.ElemLoadMesh); + + #ifdef __cplusplus + ElemLoadMeshResult result = {}; + #else + ElemLoadMeshResult result = (ElemLoadMeshResult){0}; + #endif + + return result; + } + + return listElementalToolsFunctions.ElemLoadMesh(data, meshFormat, options); } static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemBuildMeshletsOptions const * options) diff --git a/src/ElementalTools/Linux/PreCompiledHeader.h b/src/ElementalTools/Linux/PreCompiledHeader.h index 367f377b..839509a6 100644 --- a/src/ElementalTools/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Linux/PreCompiledHeader.h @@ -35,6 +35,7 @@ #include "dxcapi.h" #include "d3d12shader.h" + #include "meshoptimizer.h" #ifdef _WASDEBUG diff --git a/src/ElementalTools/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Microsoft/PreCompiledHeader.h index 654f955c..29006f26 100644 --- a/src/ElementalTools/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Microsoft/PreCompiledHeader.h @@ -36,6 +36,7 @@ using namespace Microsoft::WRL; #include "metal_irconverter/metal_irconverter.h" #include "meshoptimizer.h" +#include "fast_obj.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/tests/ToolsTests/MeshLoaderTests.cpp b/tests/ToolsTests/MeshLoaderTests.cpp new file mode 100644 index 00000000..5a6dacc9 --- /dev/null +++ b/tests/ToolsTests/MeshLoaderTests.cpp @@ -0,0 +1,74 @@ +#include "ToolsTests.h" +#include "utest.h" + +auto objMeshSourceFile = R"( + o Cube + v 1.000000 -1.000000 -1.000000 + v 1.000000 -1.000000 1.000000 + v -1.000000 -1.000000 1.000000 + v -1.000000 -1.000000 -1.000000 + v 1.000000 1.000000 -0.999999 + v 0.999999 1.000000 1.000001 + v -1.000000 1.000000 1.000000 + v -1.000000 1.000000 -1.000000 + vt 1.000000 0.333333 + vt 1.000000 0.666667 + vt 0.666667 0.666667 + vt 0.666667 0.333333 + vt 0.666667 0.000000 + vt 0.000000 0.333333 + vt 0.000000 0.000000 + vt 0.333333 0.000000 + vt 0.333333 1.000000 + vt 0.000000 1.000000 + vt 0.000000 0.666667 + vt 0.333333 0.333333 + vt 0.333333 0.666667 + vt 1.000000 0.000000 + vn 0.000000 -1.000000 0.000000 + vn 0.000000 1.000000 0.000000 + vn 1.000000 0.000000 0.000000 + vn -0.000000 0.000000 1.000000 + vn -1.000000 -0.000000 -0.000000 + vn 0.000000 0.000000 -1.000000 + s off + f 2/1/1 3/2/1 4/3/1 + f 8/1/2 7/4/2 6/5/2 + f 5/6/3 6/7/3 2/8/3 + f 6/8/4 7/5/4 3/4/4 + f 3/9/5 7/10/5 8/11/5 + f 1/12/6 4/13/6 8/11/6 + f 1/4/1 2/1/1 4/3/1 + f 5/14/2 8/1/2 6/5/2 + f 1/12/3 5/6/3 2/8/3 + f 2/12/4 6/8/4 3/4/4 + f 4/13/5 3/9/5 8/11/5 + f 5/6/6 1/12/6 8/11/6)"; + +struct MeshLoader_LoadMesh +{ + ElemMeshFormat MeshFormat; + ElemToolsDataSpan MeshSource; + uint32_t ExpectedVertexCount; +}; + +UTEST_F_SETUP(MeshLoader_LoadMesh) +{ +} + +UTEST_F_TEARDOWN(MeshLoader_LoadMesh) +{ + // Act + auto result = ElemLoadMesh(utest_fixture->MeshSource, utest_fixture->MeshFormat, NULL); + + // Assert + ASSERT_FALSE(result.HasErrors); + ASSERT_EQ_MSG(result.VertexCount, utest_fixture->ExpectedVertexCount, "LoadMesh vertex count is not correct."); +} + +UTEST_F(MeshLoader_LoadMesh, Obj) +{ + utest_fixture->MeshFormat = ElemMeshFormat_Obj; + utest_fixture->MeshSource = { .Items = (uint8_t*)objMeshSourceFile, .Length = (uint32_t)strlen(objMeshSourceFile) }; + utest_fixture->ExpectedVertexCount = 8; +} diff --git a/tests/ToolsTests/ShaderCompilerTests.cpp b/tests/ToolsTests/ShaderCompilerTests.cpp index ccffc9d7..6439d3f1 100644 --- a/tests/ToolsTests/ShaderCompilerTests.cpp +++ b/tests/ToolsTests/ShaderCompilerTests.cpp @@ -4,11 +4,10 @@ enum ShaderType { ShaderType_Unknown = 0, - ShaderType_Amplification = 1, - ShaderType_Mesh = 2, - ShaderType_Pixel = 3, - ShaderType_Compute = 4, - ShaderType_Library = 5 + ShaderType_Mesh = 1, + ShaderType_Pixel = 2, + ShaderType_Compute = 3, + ShaderType_Library = 4 }; enum ShaderMetadataType @@ -24,20 +23,6 @@ auto hlslTestSource = R"( { } - struct AmplificationPayload - { - float Test; - }; - - groupshared AmplificationPayload sharedPayload; - - [shader("amplification")] - [numthreads(1, 1, 1)] - void TestAmplification(in uint3 groupID : SV_GroupID) - { - DispatchMesh(1, 1, 1, sharedPayload); - } - struct VertexOutput { float4 Position: SV_Position; @@ -77,6 +62,7 @@ auto hlslTestSourceError = R"( } )"; + UTEST(ShaderCompiler, CanCompileShaderTest_HlslToDirectX12) { // Act @@ -122,7 +108,7 @@ struct ShaderInfo struct ShaderCompiler_CompileShader { - ElemShaderLanguage SourceLanguage; + const char* ShaderPath; ElemToolsGraphicsApi TargetGraphicsApi; ElemToolsPlatform TargetPlatform; ShaderInfo ExpectedShaders[16]; @@ -146,18 +132,6 @@ UTEST_F_SETUP(ShaderCompiler_CompileShader) .MetaDataCount = 1 }; - utest_fixture->ExpectedShaders[utest_fixture->ExpectedShaderCount++] = - { - .Type = ShaderType_Amplification, - .Name = "TestAmplification", - .Metadata = { - { - .Type = ShaderMetadataType_ThreadGroupSize, - .Value = { 1u, 1u, 1u, 0u } - }}, - .MetaDataCount = 1 - }; - utest_fixture->ExpectedShaders[utest_fixture->ExpectedShaderCount++] = { .Type = ShaderType_Mesh, @@ -181,6 +155,9 @@ UTEST_F_SETUP(ShaderCompiler_CompileShader) }}, .MetaDataCount = 1 }; + + AddTestFile("HlslTestSource.hlsl", { .Items = (uint8_t*)hlslTestSource, .Length = (uint32_t)strlen(hlslTestSource) }); + AddTestFile("HlslTestSourceError.hlsl", { .Items = (uint8_t*)hlslTestSourceError, .Length = (uint32_t)strlen(hlslTestSourceError) }); } void AssertCompilationErrors(int32_t* utest_result, ShaderCompiler_CompileShader* utest_fixture, const ElemShaderCompilationResult* compilationResult) @@ -296,21 +273,8 @@ void AssertShaderData(int32_t* utest_result, ShaderCompiler_CompileShader* utest UTEST_F_TEARDOWN(ShaderCompiler_CompileShader) { - // Arrange - auto sourceCode = !utest_fixture->WithError ? hlslTestSource : hlslTestSourceError; - - ElemShaderSourceData sourceData = - { - .ShaderLanguage = utest_fixture->SourceLanguage, - .Data = - { - .Items = (uint8_t*)sourceCode, - .Length = (uint32_t)strlen(sourceCode) - } - }; - // Act - auto compilationResult = ElemCompileShaderLibrary(utest_fixture->TargetGraphicsApi, utest_fixture->TargetPlatform, &sourceData, nullptr); + auto compilationResult = ElemCompileShaderLibrary(utest_fixture->TargetGraphicsApi, utest_fixture->TargetPlatform, utest_fixture->ShaderPath, nullptr); // Assert AssertCompilationErrors(utest_result, utest_fixture, &compilationResult); @@ -319,7 +283,7 @@ UTEST_F_TEARDOWN(ShaderCompiler_CompileShader) UTEST_F(ShaderCompiler_CompileShader, Hlsl_DirectX12_Windows) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSource.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_DirectX12; utest_fixture->TargetPlatform = ElemToolsPlatform_Windows; utest_fixture->WithError = false; @@ -327,7 +291,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_DirectX12_Windows) UTEST_F(ShaderCompiler_CompileShader, Hlsl_DirectX12_Windows_Error) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSourceError.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_DirectX12; utest_fixture->TargetPlatform = ElemToolsPlatform_Windows; utest_fixture->WithError = true; @@ -335,7 +299,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_DirectX12_Windows_Error) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Windows) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSource.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Vulkan; utest_fixture->TargetPlatform = ElemToolsPlatform_Windows; utest_fixture->WithError = false; @@ -343,7 +307,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Windows) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Windows_Error) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSourceError.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Vulkan; utest_fixture->TargetPlatform = ElemToolsPlatform_Windows; utest_fixture->WithError = true; @@ -351,7 +315,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Windows_Error) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Linux) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSource.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Vulkan; utest_fixture->TargetPlatform = ElemToolsPlatform_Linux; utest_fixture->WithError = false; @@ -359,7 +323,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Linux) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Linux_Error) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSourceError.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Vulkan; utest_fixture->TargetPlatform = ElemToolsPlatform_Linux; utest_fixture->WithError = true; @@ -368,7 +332,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Vulkan_Linux_Error) #ifndef __linux__ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_MacOS) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSource.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Metal; utest_fixture->TargetPlatform = ElemToolsPlatform_MacOS; utest_fixture->WithError = false; @@ -376,7 +340,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_MacOS) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_MacOS_Error) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSourceError.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Metal; utest_fixture->TargetPlatform = ElemToolsPlatform_MacOS; utest_fixture->WithError = true; @@ -384,7 +348,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_MacOS_Error) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_iOS) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSource.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Metal; utest_fixture->TargetPlatform = ElemToolsPlatform_iOS; utest_fixture->WithError = false; @@ -392,7 +356,7 @@ UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_iOS) UTEST_F(ShaderCompiler_CompileShader, Hlsl_Metal_iOS_Error) { - utest_fixture->SourceLanguage = ElemShaderLanguage_Hlsl; + utest_fixture->ShaderPath = "HlslTestSourceError.hlsl"; utest_fixture->TargetGraphicsApi = ElemToolsGraphicsApi_Metal; utest_fixture->TargetPlatform = ElemToolsPlatform_iOS; utest_fixture->WithError = true; diff --git a/tests/ToolsTests/ToolsTests.cpp b/tests/ToolsTests/ToolsTests.cpp new file mode 100644 index 00000000..eeda255a --- /dev/null +++ b/tests/ToolsTests/ToolsTests.cpp @@ -0,0 +1,53 @@ +#include "ToolsTests.h" + +#define MAX_TEST_FILES 256 + +struct TestFileEntry +{ + char Path[256]; + ElemToolsDataSpan Data; +}; + +static TestFileEntry GlobalTestFiles[MAX_TEST_FILES]; +static uint32_t GlobalTestFilesCount; + +void CopyString(char* destination, uint32_t destinationLength, const char* source, uint32_t sourceLength) +{ + #ifdef _WIN32 + strncpy_s(destination, destinationLength, source, sourceLength); + #else + strncpy(destination, source, sourceLength); + #endif +} + +ElemToolsDataSpan LoadTestFilesHandler(const char* path) +{ + for (uint32_t i = 0; i < GlobalTestFilesCount; i++) + { + auto testFile = GlobalTestFiles[i]; + + if (strstr(path, testFile.Path)) + { + return testFile.Data; + } + } + + return {}; +} + +void ConfigureTestFileIO() +{ + ElemToolsConfigureFileIO(LoadTestFilesHandler); +} + +void AddTestFile(const char* path, ElemToolsDataSpan data) +{ + TestFileEntry fileEntry + { + .Data = data + }; + + CopyString(fileEntry.Path, 256, path, strlen(path)); + + GlobalTestFiles[GlobalTestFilesCount++] = fileEntry; +} diff --git a/tests/ToolsTests/ToolsTests.h b/tests/ToolsTests/ToolsTests.h index c7b4ce81..1081db26 100644 --- a/tests/ToolsTests/ToolsTests.h +++ b/tests/ToolsTests/ToolsTests.h @@ -1,5 +1,7 @@ #pragma once #include "ElementalTools.h" +#include "utest.h" -// TODO: Add Utils functions +void ConfigureTestFileIO(); +void AddTestFile(const char* path, ElemToolsDataSpan data); diff --git a/tests/ToolsTests/UnityBuild.cpp b/tests/ToolsTests/UnityBuild.cpp index 67835e3c..d2522f26 100644 --- a/tests/ToolsTests/UnityBuild.cpp +++ b/tests/ToolsTests/UnityBuild.cpp @@ -1,5 +1,12 @@ +#include "ToolsTests.cpp" #include "ShaderCompilerTests.cpp" +#include "MeshLoaderTests.cpp" #include "MeshBuilderTests.cpp" -#include "utest.h" -UTEST_MAIN(); +UTEST_STATE(); + +int main(int argc, const char* argv[]) +{ + ConfigureTestFileIO(); + return utest_main(argc, argv); +} diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index 96d9eab7..cc698b94 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -1,97 +1,179 @@ -function(configure_resource_compilation target_name resource_list) +function(get_compiler_bin_path binary_name out_var) if(BUILD_FOR_IOS) - set(SHADER_COMPILER_BIN "${CMAKE_SOURCE_DIR}/build/bin/ShaderCompiler.app/Contents/MacOS/ShaderCompiler") - set(DEFAULT_SHADER_COMPILER_OPTIONS "--target-platform" "iOS") + set(bin_path "${CMAKE_SOURCE_DIR}/build/bin/${binary_name}.app/Contents/MacOS/${binary_name}") else() if(CMAKE_GENERATOR STREQUAL "Ninja") - if (APPLE) - set(SHADER_COMPILER_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ShaderCompiler.app/Contents/MacOS/ShaderCompiler") + if(APPLE) + set(bin_path "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${binary_name}.app/Contents/MacOS/${binary_name}") else() - set(SHADER_COMPILER_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ShaderCompiler/ShaderCompiler") + set(bin_path "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${binary_name}/${binary_name}") endif() else() - set(SHADER_COMPILER_BIN "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug/ShaderCompiler") + set(bin_path "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug/${binary_name}") endif() - set(DEFAULT_SHADER_COMPILER_OPTIONS "") - add_dependencies(${target_name} ShaderCompiler) - endif() - - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - list(APPEND DEFAULT_SHADER_COMPILER_OPTIONS "--debug") endif() + set(${out_var} "${bin_path}" PARENT_SCOPE) +endfunction() - # TODO: Take into account other type of resources - set(hlsl_files_list "") - set(compiled_shaders "") +function(configure_resources_for_compiler target_name result_var name binary_name source_exts dest_ext) + # Convert commas to semicolons if present + string(REPLACE "," ";" source_exts "${source_exts}") get_target_property(target_sources ${target_name} SOURCES) - - foreach(source IN LISTS target_sources) - if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${source}) - file(GLOB_RECURSE directory_hlsl_files "${CMAKE_CURRENT_SOURCE_DIR}/${source}/*.hlsl") - list(APPEND hlsl_files_list ${directory_hlsl_files}) + set(resource_files "") + foreach(src IN LISTS target_sources) + set(abs_src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") + if(IS_DIRECTORY ${abs_src}) + foreach(ext IN LISTS source_exts) + file(GLOB_RECURSE matched_files "${abs_src}/*${ext}") + list(APPEND resource_files ${matched_files}) + endforeach() else() - get_filename_component(ext ${source} EXT) - if("${ext}" STREQUAL ".hlsl") - list(APPEND hlsl_files_list ${CMAKE_CURRENT_SOURCE_DIR}/${source}) - endif() + get_filename_component(this_ext ${abs_src} EXT) + foreach(ext IN LISTS source_exts) + if("${this_ext}" STREQUAL "${ext}") + list(APPEND resource_files ${abs_src}) + break() + endif() + endforeach() endif() endforeach() - foreach(hlsl_file IN LISTS hlsl_files_list) - get_filename_component(file_path ${hlsl_file} ABSOLUTE) + if(resource_files STREQUAL "") + set(${result_var} "" PARENT_SCOPE) + return() + endif() + + get_compiler_bin_path(${binary_name} compiler_bin) + + # ARGN holds default parameters passed from the main function + set(args_to_parse ${ARGN}) + set(default_params "") + foreach(param IN LISTS args_to_parse) + if(NOT param STREQUAL "") + list(APPEND default_params ${param}) + endif() + endforeach() + + set(compiled_files "") + + foreach(file IN LISTS resource_files) + get_filename_component(file_path ${file} ABSOLUTE) get_filename_component(file_dir ${file_path} DIRECTORY) - get_filename_component(file_name ${hlsl_file} NAME_WE) + get_filename_component(file_name ${file_path} NAME_WE) file(RELATIVE_PATH relative_dir ${CMAKE_CURRENT_SOURCE_DIR} ${file_dir}) set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/${relative_dir}") file(MAKE_DIRECTORY ${output_dir}) - set(compiled_shader "${output_dir}/${file_name}.shader") - - SET(SHADER_COMPILER_OPTIONS ${DEFAULT_SHADER_COMPILER_OPTIONS}) + set(output_file "${output_dir}/${file_name}${dest_ext}") - add_custom_command(OUTPUT ${compiled_shader} - COMMAND ${SHADER_COMPILER_BIN} ${SHADER_COMPILER_OPTIONS} ${hlsl_file} ${compiled_shader} - DEPENDS ${hlsl_file} - COMMENT "Compiling HLSL shader: ${hlsl_file}" + add_custom_command( + OUTPUT ${output_file} + COMMAND ${compiler_bin} ${default_params} ${file} ${output_file} + DEPENDS ${file} + COMMENT "Compiling ${name}: ${file}" WORKING_DIRECTORY ${file_dir} ) - set_source_files_properties(${compiled_shader} PROPERTIES GENERATED TRUE) - target_sources(${target_name} PRIVATE ${compiled_shader}) + set_source_files_properties(${output_file} PROPERTIES GENERATED TRUE) + target_sources(${target_name} PRIVATE ${output_file}) + list(APPEND compiled_files ${output_file}) - list(APPEND compiled_shaders ${compiled_shader}) - - if (WIN32) - set(compiled_shader_vulkan "${output_dir}/${file_name}_vulkan.shader") - list(APPEND SHADER_COMPILER_OPTIONS "--target-api" "vulkan") - - add_custom_command(OUTPUT ${compiled_shader_vulkan} - COMMAND ${SHADER_COMPILER_BIN} ${SHADER_COMPILER_OPTIONS} ${hlsl_file} ${compiled_shader_vulkan} - DEPENDS ${hlsl_file} - COMMENT "Compiling HLSL shader for vulkan: ${hlsl_file}" + # Extra Vulkan variant for HLSL on Windows + if("${name}" STREQUAL "HLSL" AND WIN32) + set(vulkan_output_file "${output_dir}/${file_name}_vulkan${dest_ext}") + add_custom_command( + OUTPUT ${vulkan_output_file} + COMMAND ${compiler_bin} ${default_params} --target-api vulkan ${file} ${vulkan_output_file} + DEPENDS ${file} + COMMENT "Compiling ${name} (Vulkan): ${file}" WORKING_DIRECTORY ${file_dir} ) + set_source_files_properties(${vulkan_output_file} PROPERTIES GENERATED TRUE) + target_sources(${target_name} PRIVATE ${vulkan_output_file}) + list(APPEND compiled_files ${vulkan_output_file}) + endif() + endforeach() - set_source_files_properties(${compiled_shader_vulkan} PROPERTIES GENERATED TRUE) - target_sources(${target_name} PRIVATE ${compiled_shader_vulkan}) + set(${result_var} "${compiled_files}" PARENT_SCOPE) +endfunction() - list(APPEND compiled_shaders ${compiled_shader_vulkan}) +function(configure_resource_compilation target_name resource_list) + # Reintroduce original logic for iOS and Debug + if(BUILD_FOR_IOS) + set(SHADER_COMPILER_DEFAULT_OPTIONS "--target-platform iOS") + else() + set(SHADER_COMPILER_DEFAULT_OPTIONS "") + add_dependencies(${target_name} ShaderCompiler) + endif() + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + # Append debug flag to shader options + if(NOT "${SHADER_COMPILER_DEFAULT_OPTIONS}" STREQUAL "") + set(SHADER_COMPILER_DEFAULT_OPTIONS "${SHADER_COMPILER_DEFAULT_OPTIONS} --debug") + else() + set(SHADER_COMPILER_DEFAULT_OPTIONS "--debug") + endif() + endif() + + set(MESH_COMPILER_DEFAULT_OPTIONS "") + + # Use '|' as delimiters to avoid semicolon issues + # Format: Name|BinaryName|SourceExtensions|DestExtension|DefaultParams... + set(COMPILERS_LIST + "HLSL|ShaderCompiler|.hlsl|.shader|${SHADER_COMPILER_DEFAULT_OPTIONS}" + "MESH|MeshCompiler|.obj|.mesh|${MESH_COMPILER_DEFAULT_OPTIONS}" + "MESH|MeshCompiler|.gltf|.mesh|${MESH_COMPILER_DEFAULT_OPTIONS}" + ) + + set(all_compiled_resources "") + foreach(compiler_entry IN LISTS COMPILERS_LIST) + # Convert '|' to ';' to parse fields + string(REPLACE "|" ";" fields_string "${compiler_entry}") + set(fields ${fields_string}) + list(LENGTH fields fields_length) + if(fields_length LESS 4) + message(FATAL_ERROR "Invalid compiler definition: ${compiler_entry}") + endif() + + list(GET fields 0 name) + list(GET fields 1 binary_name) + list(GET fields 2 source_exts) + list(GET fields 3 dest_ext) + + if(fields_length GREATER 4) + list(SUBLIST fields 4 -1 default_params) + else() + set(default_params "") endif() + + configure_resources_for_compiler( + ${target_name} + compiled_files_for_${name} + ${name} + ${binary_name} + "${source_exts}" + ${dest_ext} + ${default_params} + ) + + list(APPEND all_compiled_resources ${compiled_files_for_${name}}) endforeach() - set(${resource_list} "${compiled_shaders}" PARENT_SCOPE) + set(${resource_list} "${all_compiled_resources}" PARENT_SCOPE) endfunction() + + function(configure_project_package target_name install_folder) cmake_parse_arguments(ARG "" "" "DEPENDENCIES;RESOURCES" ${ARGV}) list(LENGTH ARG_DEPENDENCIES dependencies_length) - message("Number of Dependencies: ${dependencies_length}") + # message("Number of Dependencies: ${dependencies_length}") list(LENGTH ARG_RESOURCES resources_length) - message("Number of resources: ${resources_length}") + # message("Number of resources: ${resources_length}") if(APPLE) set(APPFOLDER_EXTENSION ".app") @@ -170,7 +252,6 @@ function(configure_project_package target_name install_folder) ) foreach(dependency IN LISTS ARG_DEPENDENCIES) - message("Dep: ${dependency}") add_dependencies(CopyApplicationFolder${target_name} ${dependency}) add_custom_command(TARGET CopyApplicationFolder${target_name} POST_BUILD From cea20766807c9f8c5b014fdd0ec18e118973c4b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 17 Dec 2024 15:47:34 +0100 Subject: [PATCH 37/93] Add MeshLoader OBJ --- samples/Common/SampleInputs.h | 2 +- samples/Elemental/06-HelloMesh/main.c | 1 + samples/ElementalTools/02-MeshCompiler/main.c | 262 +----------------- src/Elemental/Common/SystemMemory.cpp | 6 +- .../Microsoft/SystemPlatformFunctions.cpp | 22 +- src/ElementalTools/Common/MeshLoader.cpp | 165 +++++++++-- src/ElementalTools/Common/MeshletBuilder.cpp | 27 +- src/ElementalTools/Common/ToolsUtils.cpp | 2 +- src/ElementalTools/ElementalTools.h | 17 +- src/ElementalTools/ElementalToolsLoader.c | 8 +- .../Microsoft/PreCompiledHeader.h | 1 + tests/ToolsTests/MeshLoaderTests.cpp | 31 ++- tests/ToolsTests/ToolsTests.cpp | 11 +- tests/ToolsTests/ToolsTests.h | 1 + 14 files changed, 232 insertions(+), 324 deletions(-) diff --git a/samples/Common/SampleInputs.h b/samples/Common/SampleInputs.h index 83ac7e07..32d2aebd 100644 --- a/samples/Common/SampleInputs.h +++ b/samples/Common/SampleInputs.h @@ -84,7 +84,7 @@ void SampleUpdateInputActions(SampleInputActionBindingSpan* inputActionBindings, { ElemInputEvent* inputEvent = &inputStream.Events.Items[i]; - printf("Input Event: %d, %f\n", inputEvent->InputId, inputEvent->Value); + //printf("Input Event: %d, %f\n", inputEvent->InputId, inputEvent->Value); for (uint32_t j = 0; j < inputActionBindings->Length; j++) { diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index cd3aa918..bc022b00 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -156,6 +156,7 @@ void InitSample(void* payload) CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); LoadMesh(&applicationPayload->TestMeshData, "kitten.mesh", applicationPayload); + //LoadMesh(&applicationPayload->TestMeshData, "buddha.mesh", applicationPayload); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index ee4077dd..c09122c0 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -1,248 +1,7 @@ #include "ElementalTools.h" #include "SampleUtils.h" -#include "SampleMath.h" #include "SampleMesh.h" -/** - * WARNING: This obj parsing code is just for demonstration purpose only. The parsing is not feature complete - * and the implementation focus on readability and not performance. Don't use it in your production code! - */ - -typedef struct -{ - uint32_t SubObjectCount; - uint32_t VertexCount; - uint32_t NormalCount; - uint32_t TextCoordCount; - uint32_t FaceCount; -} ObjMeshElementCount; - -typedef struct -{ - uint32_t VertexIndex; - uint32_t TextCoordIndex; - uint32_t NormalIndex; -} ObjMeshVertex; - -typedef struct -{ - union - { - ObjMeshVertex Vertex; - uint32_t Indices[3]; - } Elements[3]; -} ObjMeshFace; - -typedef struct -{ - SampleVector3 Position; - SampleVector3 Normal; - SampleVector2 TextureCoordinates; -} InputMeshVertex; - -typedef struct -{ - InputMeshVertex* VertexList; - uint32_t VertexCount; -} InputMeshData; - -ObjMeshElementCount CountObjMeshElements(ElemToolsDataSpan data) -{ - ElemToolsDataSpan line = SampleReadLine(&data); - - ObjMeshElementCount result = { .VertexCount = 1, .NormalCount = 1, .TextCoordCount = 1 }; - - while (line.Length > 0) - { - if (line.Length > 2) - { - if (line.Items[0] == 'g') - { - result.SubObjectCount++; - } - else if (line.Items[0] == 'v' && line.Items[1] == 'n') - { - result.NormalCount++; - } - else if (line.Items[0] == 'v' && line.Items[1] == 't') - { - result.TextCoordCount++; - } - else if (line.Items[0] == 'v') - { - result.VertexCount++; - } - else if (line.Items[0] == 'f') - { - result.FaceCount++; - } - } - - line = SampleReadLine(&data); - } - - printf("SubObject: %d\n", result.SubObjectCount); - printf("Vextex: %d\n", result.VertexCount); - printf("Normal: %d\n", result.NormalCount); - printf("TestCoord: %d\n", result.TextCoordCount); - printf("Face: %d\n", result.FaceCount); - - return result; -} - -SampleVector2 ReadObjVector2(ElemToolsDataSpan* lineParts, uint32_t linePartCount) -{ - if (linePartCount < 3) - { - printf("Error: Invalid number of elements in the line for Vector2.\n"); - return (SampleVector2) { 0 }; - } - - float x = atof((const char*)lineParts[1].Items); - float y = atof((const char*)lineParts[2].Items); - - return (SampleVector2) - { - .X = x, - .Y = y - }; -} - -SampleVector3 ReadObjVector3(ElemToolsDataSpan* lineParts, uint32_t linePartCount, bool reverseHandedness) -{ - if (linePartCount < 4) - { - printf("Error: Invalid number of elements in the line for Vector3.\n"); - return (SampleVector3) { 0 }; - } - - float x = atof((const char*)lineParts[1].Items); - float y = atof((const char*)lineParts[2].Items); - float z = atof((const char*)lineParts[3].Items); - - if (reverseHandedness) - { - z = -z; - } - - return (SampleVector3) - { - .X = x, - .Y = y, - .Z = z - }; -} - -ObjMeshFace ReadObjFace(ElemToolsDataSpan* lineParts, uint32_t linePartCount, bool reverseHandedness) -{ - if (linePartCount < 4) - { - printf("Error: Invalid number of elements in the line for Face.\n"); - return (ObjMeshFace) {}; - } - - ObjMeshFace result = {}; - - for (uint32_t i = 0; i < 3; i++) - { - ElemToolsDataSpan faceParts[3]; - uint32_t facePartCount = 0; - - SampleSplitString(faceParts, &facePartCount, lineParts[i + 1], '/'); - - for (uint32_t j = 0; j < 3; j++) - { - if (faceParts[j].Length > 0) - { - // Note: We are not handling negative indices - result.Elements[i].Indices[j] = atoi((const char*)faceParts[j].Items); - } - } - } - - if (reverseHandedness) - { - ObjMeshVertex temp = result.Elements[1].Vertex; - result.Elements[1] = result.Elements[2]; - result.Elements[2].Vertex = temp; - } - - return result; -} - -InputMeshData ReadObjMesh(ElemToolsDataSpan data, bool reverseHandedness) -{ - ObjMeshElementCount meshElementCount = CountObjMeshElements(data); - ElemToolsDataSpan line = SampleReadLine(&data); - - SampleVector3* vertexList = malloc(meshElementCount.VertexCount * sizeof(SampleVector3)); - memset(vertexList, 0, meshElementCount.VertexCount * sizeof(SampleVector3)); - uint32_t vertexCount = 1; - - SampleVector3* normalList = malloc(meshElementCount.NormalCount * sizeof(SampleVector3)); - memset(normalList, 0, meshElementCount.NormalCount * sizeof(SampleVector3)); - uint32_t normalCount = 1; - - SampleVector2* textCoordList = malloc(meshElementCount.TextCoordCount * sizeof(SampleVector2)); - memset(textCoordList, 0, meshElementCount.TextCoordCount * sizeof(SampleVector2)); - uint32_t textCoordCount = 1; - - InputMeshVertex* inputMeshVertexList = malloc(meshElementCount.FaceCount * 3 * sizeof(InputMeshVertex)); - uint32_t inputMeshVertexCount = 0; - - while (line.Length > 0) - { - ElemToolsDataSpan lineParts[64]; - uint32_t linePartCount = 0; - SampleSplitString(lineParts, &linePartCount, line, ' '); - - if (linePartCount > 1) - { - if (SampleCompareString(lineParts[0], "g")) - { - printf("ERROR: Group not implemented yet!\n"); - return (InputMeshData) { 0 }; - } - else if (SampleCompareString(lineParts[0], "v")) - { - vertexList[vertexCount++] = ReadObjVector3(lineParts, linePartCount, reverseHandedness); - } - else if (SampleCompareString(lineParts[0], "vn")) - { - normalList[normalCount++] = ReadObjVector3(lineParts, linePartCount, reverseHandedness); - } - else if (SampleCompareString(lineParts[0], "vt")) - { - textCoordList[textCoordCount++] = ReadObjVector2(lineParts, linePartCount); - } - else if (SampleCompareString(lineParts[0], "f")) - { - ObjMeshFace face = ReadObjFace(lineParts, linePartCount, reverseHandedness); - - for (uint32_t i = 0; i < 3; i++) - { - InputMeshVertex vertex = - { - .Position = vertexList[face.Elements[i].Vertex.VertexIndex], - .Normal = normalList[face.Elements[i].Vertex.NormalIndex], - .TextureCoordinates = textCoordList[face.Elements[i].Vertex.TextCoordIndex] - }; - - inputMeshVertexList[inputMeshVertexCount++] = vertex; - } - } - } - - line = SampleReadLine(&data); - } - - return (InputMeshData) - { - .VertexList = inputMeshVertexList, - .VertexCount = inputMeshVertexCount - }; -} - int main(int argc, const char* argv[]) { // TODO: Add an option to handle handness change @@ -303,27 +62,10 @@ int main(int argc, const char* argv[]) return 1; } - ElemLoadMeshResult inputMesh = ElemLoadMesh(inputData, ElemMeshFormat_Obj, NULL); + ElemLoadMeshResult inputMesh = ElemLoadMesh(inputPath, &(ElemLoadMeshOptions) { .MeshCoordinateSystem = ElemMeshCoordinateSystem_LeftHanded }); printf("Input mesh vertex Count: %d\n", inputMesh.VertexCount); - InputMeshData inputMeshData = ReadObjMesh(inputData, true); - printf("Input mesh vertex Count: %d\n", inputMeshData.VertexCount); - - // TODO: Temporary - if (inputMeshData.VertexCount == 0) - { - uint8_t test = { 1 }; - SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = &test, .Length = 1 }, false); - return 0; - } - - ElemVertexBuffer vertexBuffer = - { - .Data = { .Items = (uint8_t*)inputMeshData.VertexList, .Length = inputMeshData.VertexCount * sizeof(InputMeshVertex) }, - .VertexSize = sizeof(InputMeshVertex) - }; - - ElemBuildMeshletResult result = ElemBuildMeshlets(vertexBuffer, NULL); + ElemBuildMeshletResult result = ElemBuildMeshlets(inputMesh.VertexBuffer, NULL); // TODO: Refactor this into an util function to display messages with proper colors for (uint32_t i = 0; i < result.Messages.Length; i++) diff --git a/src/Elemental/Common/SystemMemory.cpp b/src/Elemental/Common/SystemMemory.cpp index 73be00f8..8367b6d2 100644 --- a/src/Elemental/Common/SystemMemory.cpp +++ b/src/Elemental/Common/SystemMemory.cpp @@ -5,7 +5,7 @@ #ifdef ElemAPI #include "SystemLogging.h" #else -#define SystemLogErrorMessage(category, format, ...) +#define SystemLogErrorMessage(category, format, ...) printf(format __VA_OPT__(,) __VA_ARGS__) #endif #define MEMORYARENA_DEFAULT_SIZE 64 * 1024 * 1024 @@ -431,7 +431,7 @@ void* SystemPushMemory(MemoryArena memoryArena, size_t sizeInBytes, AllocationSt if (allocatedSize + sizeInBytes > storage->SizeInBytes) { - SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot push to memory arena with: %u (Allocated size is: %u, Max size is: %u)", sizeInBytes, allocatedSize, storage->SizeInBytes); + SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot push to memory arena with: %u (Allocated size is: %u, Max size is: %u)", (uint32_t)sizeInBytes, (uint32_t)allocatedSize, (uint32_t)storage->SizeInBytes); return nullptr; } @@ -462,7 +462,7 @@ void SystemPopMemory(MemoryArena memoryArena, size_t sizeInBytes) if (sizeInBytes > allocatedSize) { - SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot pop memory arena with: %u (Allocated size is: %u)", sizeInBytes, allocatedSize); + SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot pop memory arena with: %u (Allocated size is: %u)", (uint32_t)sizeInBytes, (uint32_t)allocatedSize); return; } diff --git a/src/Elemental/Microsoft/SystemPlatformFunctions.cpp b/src/Elemental/Microsoft/SystemPlatformFunctions.cpp index 9fdef14b..709a9add 100644 --- a/src/Elemental/Microsoft/SystemPlatformFunctions.cpp +++ b/src/Elemental/Microsoft/SystemPlatformFunctions.cpp @@ -4,7 +4,9 @@ #ifdef ElemAPI #include "SystemLogging.h" #else +#ifndef SystemLogErrorMessage #define SystemLogErrorMessage(category, format, ...) +#endif #endif SystemPlatformAllocationInfos systemPlatformAllocationInfos; @@ -126,7 +128,7 @@ size_t SystemPlatformFileGetSizeInBytes(ReadOnlySpan path) if (fileHandle == INVALID_HANDLE_VALUE) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for reading. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for reading. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); return 0; } @@ -145,14 +147,14 @@ void SystemPlatformFileWriteBytes(ReadOnlySpan path, ReadOnlySpan if (fileHandle == INVALID_HANDLE_VALUE) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for writing. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for writing. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); return; } DWORD bytesWritten; if (!WriteFile(fileHandle, data.Pointer, data.Length, &bytesWritten, nullptr) || bytesWritten != data.Length) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Error writing to file %s. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Error writing to file %s. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); } CloseHandle(fileHandle); @@ -167,14 +169,14 @@ void SystemPlatformFileReadBytes(ReadOnlySpan path, Span data) if (fileHandle == INVALID_HANDLE_VALUE) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for reading. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open file %s for reading. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); return; } DWORD bytesRead; if (!ReadFile(fileHandle, data.Pointer, data.Length, &bytesRead, nullptr) || bytesRead != data.Length) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Error reading file %s. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Error reading file %s. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); } CloseHandle(fileHandle); @@ -187,7 +189,7 @@ void SystemPlatformFileDelete(ReadOnlySpan path) if (!DeleteFile(pathWide.Pointer)) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot delete file %s. (Error code: %d)", path.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot delete file %s. (Error code: %d)", path.Pointer, (int32_t)GetLastError()); } } @@ -204,7 +206,7 @@ ReadOnlySpan SystemPlatformExecuteProcess(MemoryArena memoryArena, ReadOnl HANDLE readPipe, writePipe; if (!CreatePipe(&readPipe, &writePipe, &pipeAttributes, 0)) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open read/write pipe for launching command: %s (Error code: %d)", command.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot open read/write pipe for launching command: %s (Error code: %d)", command.Pointer, (int32_t)GetLastError()); return ReadOnlySpan(); } @@ -217,7 +219,7 @@ ReadOnlySpan SystemPlatformExecuteProcess(MemoryArena memoryArena, ReadOnl PROCESS_INFORMATION processInfo {}; if (!CreateProcess(nullptr, (LPWSTR)commandWide.Pointer, nullptr, nullptr, true, 0, nullptr, nullptr, &startupInfo, &processInfo)) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot create process for launching command: %s (Error code: %d)", command.Pointer, GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot create process for launching command: %s (Error code: %d)", command.Pointer, (int32_t)GetLastError()); return ReadOnlySpan(); } @@ -266,7 +268,7 @@ void* SystemPlatformCreateThread(void* threadFunction, void* parameters) if (threadHandle == nullptr) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot create thread (Error code: %d)", GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Cannot create thread (Error code: %d)", (int32_t)GetLastError()); return nullptr; } @@ -285,7 +287,7 @@ void SystemPlatformWaitThread(void* thread) if (waitResult != WAIT_OBJECT_0) { - SystemLogErrorMessage(ElemLogMessageCategory_Application, "Failed to wait on thread (Error code: %d)", GetLastError()); + SystemLogErrorMessage(ElemLogMessageCategory_Application, "Failed to wait on thread (Error code: %d)", (int32_t)GetLastError()); } } diff --git a/src/ElementalTools/Common/MeshLoader.cpp b/src/ElementalTools/Common/MeshLoader.cpp index 80d53182..51386c1d 100644 --- a/src/ElementalTools/Common/MeshLoader.cpp +++ b/src/ElementalTools/Common/MeshLoader.cpp @@ -1,19 +1,31 @@ - +#include "ToolsUtils.h" #include "ElementalTools.h" #include "SystemMemory.h" #include "SystemFunctions.h" +// TODO: Do one for each thread +static MemoryArena MeshLoaderMemoryArena; -// TODO: We need to track the offset +struct ObjLoaderFileData +{ + ReadOnlySpan FileData; + uint32_t CurrentOffset; +}; void* FastObjFileOpen(const char* path, void* userData) { - if (SystemFindSubString(path, "ObjData") != -1) + auto objFileData = LoadFileData(path); + + if (objFileData.Length == 0) { return nullptr; } - return (void*)1; + auto objLoaderFileData = SystemPushStruct(*(MemoryArena*)userData); + objLoaderFileData->CurrentOffset = 0; + objLoaderFileData->FileData = objFileData; + + return objLoaderFileData; } void FastObjFileClose(void* file, void* userData) @@ -22,26 +34,143 @@ void FastObjFileClose(void* file, void* userData) size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userData) { - // TODO: Check File ID - auto sourceDataSpan = (ElemToolsDataSpan*)userData; - auto sourceSpan = ReadOnlySpan(sourceDataSpan->Items, sourceDataSpan->Length); + SystemAssert(file); + + auto objLoaderFileData = (ObjLoaderFileData*)file; + auto sourceSpan = objLoaderFileData->FileData; auto destinationSpan = Span((uint8_t*)destination, bytes); - - printf("ReadObj: %d/%d\n", bytes, sourceDataSpan->Length); - SystemCopyBuffer(destinationSpan, sourceSpan); - return 0; + auto readLength = SystemMin(sourceSpan.Length - objLoaderFileData->CurrentOffset, bytes); + + if (readLength > 0) + { + //printf("ReadObj: %d/%d\n", (uint32_t)bytes, (uint32_t)readLength); + SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); + } + + objLoaderFileData->CurrentOffset += readLength; + return readLength; } unsigned long FastObjFileSize(void* file, void* userData) { - // TODO: Check File ID - auto dataSpan = (ElemToolsDataSpan*)userData; - return dataSpan->Length; + SystemAssert(file); + + auto objLoaderFileData = (ObjLoaderFileData*)file; + return objLoaderFileData->FileData.Length; +} + +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemMeshCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) +{ + // TODO: Check what to include + auto currentVertexBufferPointer = *vertexBufferPointer; + + if (objVertex.p) + { + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; + } + + if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + + currentVertexBufferPointer += 3 * sizeof(float); + } + + if (objVertex.n) + { + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->normals[3 * objVertex.n + j]; + } + + if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + + currentVertexBufferPointer += 3 * sizeof(float); + } + + if (objVertex.t) + { + for (uint32_t j = 0; j < 2; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->texcoords[2 * objVertex.t + j]; + } + } + + // TODO: Force the texture coordinates for now + currentVertexBufferPointer += 2 * sizeof(float); + *vertexBufferPointer = currentVertexBufferPointer; +} + +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ElemLoadMeshOptions* options) +{ + // TODO: Handle sub objects + ElemMeshCoordinateSystem coordinateSystem = ElemMeshCoordinateSystem_LeftHanded; + + if (options != nullptr) + { + coordinateSystem = options->MeshCoordinateSystem; + } + + printf("Coord: %d\n", coordinateSystem); + + auto positionSize = sizeof(float) * 3; + auto normalSize = sizeof(float) * 3; + auto textureCoordinatesSize = sizeof(float) * 2; + auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; + + // TODO: Temporary + auto realVertexSize = maxVertexSize; + + auto vertexBuffer = SystemPushArray(memoryArena, objMesh->index_count * maxVertexSize); + auto currentVertexBufferPointer = vertexBuffer.Pointer; + + SystemAssert((objMesh->index_count % 3) == 0); + + for (uint32_t i = 0; i < objMesh->index_count; i += 3) + { + if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + { + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); + } + else + { + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); + } + } + + return + { + .Data = { .Items = vertexBuffer.Pointer, .Length = objMesh->index_count * (uint32_t)realVertexSize }, + .VertexSize = (uint32_t)realVertexSize + }; } -ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, const ElemLoadMeshOptions* options) +void InitMeshLoaderMemoryArena() { + if (MeshLoaderMemoryArena.Storage == nullptr) + { + MeshLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + } + + SystemClearMemoryArena(MeshLoaderMemoryArena); +} + +// TODO: Split object loaders into multiple files +ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(const char* path, const ElemLoadMeshOptions* options) +{ + InitMeshLoaderMemoryArena(); + // TODO: To output the results, we should use a separate memory arena for each modules auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -55,10 +184,14 @@ ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFor .file_size = FastObjFileSize }; - auto mesh = fast_obj_read_with_callbacks("ObjFile", &callbacks, (void*)&data); + auto mesh = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); + + auto vertexBuffer = ConstructObjVertexBuffer(MeshLoaderMemoryArena, mesh, options); return { + .VertexBuffer = vertexBuffer, + .VertexCount = mesh->index_count, .HasErrors = hasErrors }; } diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Common/MeshletBuilder.cpp index 6a766842..4b8a1aab 100644 --- a/src/ElementalTools/Common/MeshletBuilder.cpp +++ b/src/ElementalTools/Common/MeshletBuilder.cpp @@ -1,8 +1,23 @@ #include "ElementalTools.h" #include "SystemMemory.h" +// TODO: Do one for each thread +static MemoryArena MeshletBuilderMemoryArena; + +void InitMeshletBuilderMemoryArena() +{ + if (MeshletBuilderMemoryArena.Storage == nullptr) + { + MeshletBuilderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + } + + SystemClearMemoryArena(MeshletBuilderMemoryArena); +} + ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options) { + InitMeshletBuilderMemoryArena(); + // TODO: Error messages auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -12,7 +27,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, nullptr, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); - auto vertexList = SystemPushArrayZero(stackMemoryArena, vertexCount * vertexBuffer.VertexSize); + auto vertexList = SystemPushArrayZero(MeshletBuilderMemoryArena, vertexCount * vertexBuffer.VertexSize); auto indexList = SystemPushArrayZero(stackMemoryArena, indexCount); meshopt_remapVertexBuffer(vertexList.Pointer, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize, vertexRemap.Pointer); @@ -32,9 +47,9 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto meshletCount = (uint32_t)meshopt_buildMeshletsBound(indexCount, meshletMaxVertexCount, meshletMaxTriangleCount); auto meshOptMeshletList = SystemPushArray(stackMemoryArena, meshletCount); - auto meshletVertexIndexList = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxVertexCount); + auto meshletVertexIndexList = SystemPushArray(MeshletBuilderMemoryArena, meshletCount * meshletMaxVertexCount); auto meshletTriangleIndexListRaw = SystemPushArray(stackMemoryArena, meshletCount * meshletMaxTriangleCount * 3); - auto meshletTriangleIndexList = SystemPushArrayZero(stackMemoryArena, meshletCount * meshletMaxTriangleCount); + auto meshletTriangleIndexList = SystemPushArrayZero(MeshletBuilderMemoryArena, meshletCount * meshletMaxTriangleCount); meshletCount = meshopt_buildMeshlets(meshOptMeshletList.Pointer, meshletVertexIndexList.Pointer, @@ -54,7 +69,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf //printf("MeshletCount: %d\n", meshletCount); - auto meshletList = SystemPushArray(stackMemoryArena, meshletCount); + auto meshletList = SystemPushArray(MeshletBuilderMemoryArena, meshletCount); for (uint32_t i = 0; i < meshletCount; i++) { @@ -65,12 +80,12 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf meshlet.triangle_count, meshlet.vertex_count); - auto meshletBounds = meshopt_computeMeshletBounds(&meshletVertexIndexList[meshlet.vertex_offset], + /*auto meshletBounds = meshopt_computeMeshletBounds(&meshletVertexIndexList[meshlet.vertex_offset], &meshletTriangleIndexListRaw[meshlet.triangle_offset], meshlet.triangle_count, (const float*)vertexList.Pointer, vertexCount, - vertexBuffer.VertexSize); + vertexBuffer.VertexSize);*/ // TODO: Cone and sphere diff --git a/src/ElementalTools/Common/ToolsUtils.cpp b/src/ElementalTools/Common/ToolsUtils.cpp index af4af472..74d82265 100644 --- a/src/ElementalTools/Common/ToolsUtils.cpp +++ b/src/ElementalTools/Common/ToolsUtils.cpp @@ -29,7 +29,7 @@ void InitStorageMemoryArena() { if (FileIOMemoryArena.Storage == nullptr) { - FileIOMemoryArena = SystemAllocateMemoryArena(); + FileIOMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); } } diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 388fbfa9..9bc8463f 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -212,20 +212,29 @@ typedef enum ElemMeshFormat_Obj = 0, } ElemMeshFormat; +typedef enum +{ + ElemMeshCoordinateSystem_LeftHanded = 0, + ElemMeshCoordinateSystem_RightHanded = 1 +} ElemMeshCoordinateSystem; + typedef struct { ElemToolsDataSpan Data; uint32_t VertexSize; + // TODO: Add vertex description structure } ElemVertexBuffer; typedef struct { - uint32_t Reserved; + ElemMeshCoordinateSystem MeshCoordinateSystem; + // TODO: Add options to specify the vertex format wanted } ElemLoadMeshOptions; // TODO: Allow to specify the offset of the vertex position? (For now we assume vertex position is at offset 0) typedef struct { + // TODO: Allow bypass mesh format uint32_t Reserved; } ElemBuildMeshletsOptions; @@ -251,6 +260,7 @@ typedef struct typedef struct { + ElemMeshFormat MeshFormat; ElemVertexBuffer VertexBuffer; uint32_t VertexCount; ElemToolsMessageSpan Messages; @@ -269,10 +279,7 @@ typedef struct bool HasErrors; } ElemBuildMeshletResult; -// TODO: Add a way to pass multiple files? -// TODO: We need to have also a callback system for providing data based on files. Because for shaders and meshes you don't know -// in advance what files are used. The model of fast_obj is actually pretty good -ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, const ElemLoadMeshOptions* options); +ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(const char* path, const ElemLoadMeshOptions* options); ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index a8dd59ab..ec20d662 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -26,7 +26,7 @@ typedef struct ElementalToolsFunctions void (*ElemToolsConfigureFileIO)(ElemToolsLoadFileHandlerPtr); bool (*ElemCanCompileShader)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform); ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *); - ElemLoadMeshResult (*ElemLoadMesh)(ElemToolsDataSpan, ElemMeshFormat, ElemLoadMeshOptions const *); + ElemLoadMeshResult (*ElemLoadMesh)(char const *, ElemLoadMeshOptions const *); ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemBuildMeshletsOptions const *); } ElementalToolsFunctions; @@ -83,7 +83,7 @@ static bool LoadElementalToolsFunctionPointers(void) listElementalToolsFunctions.ElemToolsConfigureFileIO = (void (*)(ElemToolsLoadFileHandlerPtr))GetElementalToolsFunctionPointer("ElemToolsConfigureFileIO"); listElementalToolsFunctions.ElemCanCompileShader = (bool (*)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform))GetElementalToolsFunctionPointer("ElemCanCompileShader"); listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); - listElementalToolsFunctions.ElemLoadMesh = (ElemLoadMeshResult (*)(ElemToolsDataSpan, ElemMeshFormat, ElemLoadMeshOptions const *))GetElementalToolsFunctionPointer("ElemLoadMesh"); + listElementalToolsFunctions.ElemLoadMesh = (ElemLoadMeshResult (*)(char const *, ElemLoadMeshOptions const *))GetElementalToolsFunctionPointer("ElemLoadMesh"); listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); @@ -170,7 +170,7 @@ static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGrap return listElementalToolsFunctions.ElemCompileShaderLibrary(graphicsApi, platform, path, options); } -static inline ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFormat meshFormat, ElemLoadMeshOptions const * options) +static inline ElemLoadMeshResult ElemLoadMesh(char const * path, ElemLoadMeshOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { @@ -198,7 +198,7 @@ static inline ElemLoadMeshResult ElemLoadMesh(ElemToolsDataSpan data, ElemMeshFo return result; } - return listElementalToolsFunctions.ElemLoadMesh(data, meshFormat, options); + return listElementalToolsFunctions.ElemLoadMesh(path, options); } static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemBuildMeshletsOptions const * options) diff --git a/src/ElementalTools/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Microsoft/PreCompiledHeader.h index 29006f26..c5553c62 100644 --- a/src/ElementalTools/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Microsoft/PreCompiledHeader.h @@ -1,4 +1,5 @@ #pragma once +#define _CRT_SECURE_NO_WARNINGS #include #include diff --git a/tests/ToolsTests/MeshLoaderTests.cpp b/tests/ToolsTests/MeshLoaderTests.cpp index 5a6dacc9..4c50b379 100644 --- a/tests/ToolsTests/MeshLoaderTests.cpp +++ b/tests/ToolsTests/MeshLoaderTests.cpp @@ -1,8 +1,7 @@ #include "ToolsTests.h" #include "utest.h" -auto objMeshSourceFile = R"( - o Cube +auto cubeObjMeshSource = R"(o Cube v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 1.000000 v -1.000000 -1.000000 1.000000 @@ -47,28 +46,44 @@ auto objMeshSourceFile = R"( struct MeshLoader_LoadMesh { - ElemMeshFormat MeshFormat; - ElemToolsDataSpan MeshSource; + const char* Path; uint32_t ExpectedVertexCount; }; UTEST_F_SETUP(MeshLoader_LoadMesh) { + AddTestFile("Cube.obj", { .Items = (uint8_t*)cubeObjMeshSource, .Length = (uint32_t)strlen(cubeObjMeshSource) }); } UTEST_F_TEARDOWN(MeshLoader_LoadMesh) { // Act - auto result = ElemLoadMesh(utest_fixture->MeshSource, utest_fixture->MeshFormat, NULL); + auto result = ElemLoadMesh(utest_fixture->Path, NULL); // Assert ASSERT_FALSE(result.HasErrors); ASSERT_EQ_MSG(result.VertexCount, utest_fixture->ExpectedVertexCount, "LoadMesh vertex count is not correct."); + ASSERT_GT_MSG(result.VertexBuffer.VertexSize, 0u, "Vertex size must be greater than 0."); + ASSERT_GT_MSG(result.VertexBuffer.Data.Length, 0u, "VertexBuffer size must be greater than 0."); + + bool isEmpty = true; + + for (uint32_t i = 0; i < result.VertexBuffer.Data.Length; i++) + { + if (result.VertexBuffer.Data.Items[i] != 0) + { + isEmpty = false; + break; + } + } + + ASSERT_FALSE_MSG(isEmpty, "VertexBuffer must contains data."); + + // TODO: Add more data checks based on the format } UTEST_F(MeshLoader_LoadMesh, Obj) { - utest_fixture->MeshFormat = ElemMeshFormat_Obj; - utest_fixture->MeshSource = { .Items = (uint8_t*)objMeshSourceFile, .Length = (uint32_t)strlen(objMeshSourceFile) }; - utest_fixture->ExpectedVertexCount = 8; + utest_fixture->Path = "Cube.obj"; + utest_fixture->ExpectedVertexCount = 36; } diff --git a/tests/ToolsTests/ToolsTests.cpp b/tests/ToolsTests/ToolsTests.cpp index eeda255a..88a6a569 100644 --- a/tests/ToolsTests/ToolsTests.cpp +++ b/tests/ToolsTests/ToolsTests.cpp @@ -11,15 +11,6 @@ struct TestFileEntry static TestFileEntry GlobalTestFiles[MAX_TEST_FILES]; static uint32_t GlobalTestFilesCount; -void CopyString(char* destination, uint32_t destinationLength, const char* source, uint32_t sourceLength) -{ - #ifdef _WIN32 - strncpy_s(destination, destinationLength, source, sourceLength); - #else - strncpy(destination, source, sourceLength); - #endif -} - ElemToolsDataSpan LoadTestFilesHandler(const char* path) { for (uint32_t i = 0; i < GlobalTestFilesCount; i++) @@ -47,7 +38,7 @@ void AddTestFile(const char* path, ElemToolsDataSpan data) .Data = data }; - CopyString(fileEntry.Path, 256, path, strlen(path)); + strncpy(fileEntry.Path, path, strlen(path)); GlobalTestFiles[GlobalTestFilesCount++] = fileEntry; } diff --git a/tests/ToolsTests/ToolsTests.h b/tests/ToolsTests/ToolsTests.h index 1081db26..57a2cdb0 100644 --- a/tests/ToolsTests/ToolsTests.h +++ b/tests/ToolsTests/ToolsTests.h @@ -1,4 +1,5 @@ #pragma once +#define _CRT_SECURE_NO_WARNINGS #include "ElementalTools.h" #include "utest.h" From 0ce717678f6b271024e0674676aed05dbbbd9021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 19 Dec 2024 14:19:20 +0100 Subject: [PATCH 38/93] Update camera controls --- REFERENCES.MD | 5 + ...tionInputs.h => SampleInputsApplication.h} | 18 +- samples/Common/SampleInputsCamera.h | 186 ++++++++++ ...ewerInputs.h => SampleInputsModelViewer.h} | 30 +- samples/Common/SampleMath.h | 146 +++++++- samples/Elemental/04-HelloCompute/main.c | 6 +- .../06-HelloMesh/Data/RenderMesh.hlsl | 2 + samples/Elemental/06-HelloMesh/main.c | 26 +- samples/Elemental/99-Renderer/CMakeLists.txt | 11 + .../99-Renderer/Data/RenderMesh.hlsl | 130 +++++++ samples/Elemental/99-Renderer/main.c | 326 ++++++++++++++++++ samples/ElementalTools/02-MeshCompiler/main.c | 12 +- src/Elemental/Elemental.h | 2 +- src/Elemental/Microsoft/Win32Inputs.cpp | 1 + .../Common/DirectXShaderCompiler.cpp | 3 + src/ElementalTools/Common/MeshLoader.cpp | 48 ++- src/ElementalTools/Common/UnityBuild.cpp | 3 +- src/ElementalTools/ElementalTools.h | 75 ++-- src/ElementalTools/ElementalToolsLoader.c | 20 +- tests/GraphicsTests/GraphicsTests.h | 4 +- tests/ToolsTests/MeshLoaderTests.cpp | 16 +- 21 files changed, 932 insertions(+), 138 deletions(-) create mode 100644 REFERENCES.MD rename samples/Common/{SampleApplicationInputs.h => SampleInputsApplication.h} (72%) create mode 100644 samples/Common/SampleInputsCamera.h rename samples/Common/{SampleModelViewerInputs.h => SampleInputsModelViewer.h} (94%) create mode 100644 samples/Elemental/99-Renderer/CMakeLists.txt create mode 100644 samples/Elemental/99-Renderer/Data/RenderMesh.hlsl create mode 100644 samples/Elemental/99-Renderer/main.c diff --git a/REFERENCES.MD b/REFERENCES.MD new file mode 100644 index 00000000..45996388 --- /dev/null +++ b/REFERENCES.MD @@ -0,0 +1,5 @@ +https://guide.handmadehero.org/ +https://github.com/zeux/niagara + +https://therealmjp.github.io/posts/breaking-down-barriers-part-1-whats-a-barrier/ +https://therealmjp.github.io/posts/gpu-memory-pool/ diff --git a/samples/Common/SampleApplicationInputs.h b/samples/Common/SampleInputsApplication.h similarity index 72% rename from samples/Common/SampleApplicationInputs.h rename to samples/Common/SampleInputsApplication.h index 02b096eb..a2bb06e5 100644 --- a/samples/Common/SampleApplicationInputs.h +++ b/samples/Common/SampleInputsApplication.h @@ -7,7 +7,7 @@ typedef struct { float SwitchShowCursor; float ExitApp; -} SampleApplicationInputActions; +} SampleInputsApplicationActions; typedef struct { @@ -15,16 +15,16 @@ typedef struct bool ShowCursor; bool HideCursor; bool IsCursorDisplayed; -} SampleApplicationInputState; +} SampleInputsApplicationState; typedef struct { - SampleApplicationInputActions InputActions; + SampleInputsApplicationActions InputActions; SampleInputActionBindingSpan InputActionBindings; - SampleApplicationInputState State; -} SampleApplicationInputs; + SampleInputsApplicationState State; +} SampleInputsApplication; -void SampleApplicationInputsInit(SampleApplicationInputs* inputs) +void SampleInputsApplicationInit(SampleInputsApplication* inputs) { SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyF1, 0, SampleInputActionBindingType_Released, &inputs->InputActions.SwitchShowCursor); SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyEscape, 0, SampleInputActionBindingType_Released, &inputs->InputActions.ExitApp); @@ -34,10 +34,10 @@ void SampleApplicationInputsInit(SampleApplicationInputs* inputs) inputs->State.IsCursorDisplayed = true; } -void SampleApplicationInputsUpdate(ElemInputStream inputStream, SampleApplicationInputs* inputs, float deltaTimeInSeconds) +void SampleInputsApplicationUpdate(ElemInputStream inputStream, SampleInputsApplication* inputs, float deltaTimeInSeconds) { - SampleApplicationInputState* state = &inputs->State; - SampleApplicationInputActions* inputActions = &inputs->InputActions; + SampleInputsApplicationState* state = &inputs->State; + SampleInputsApplicationActions* inputActions = &inputs->InputActions; SampleUpdateInputActions(&inputs->InputActionBindings, inputStream); diff --git a/samples/Common/SampleInputsCamera.h b/samples/Common/SampleInputsCamera.h new file mode 100644 index 00000000..5b719666 --- /dev/null +++ b/samples/Common/SampleInputsCamera.h @@ -0,0 +1,186 @@ +#pragma once + +#include "Elemental.h" +#include "SampleInputs.h" +#include "SampleMath.h" + +typedef struct +{ + float MoveLeft; + float MoveRight; + float MoveForward; + float MoveBackward; + + float RotateLeft; + float RotateRight; + float RotateUp; + float RotateDown; + float RotateSideLeft; + float RotateSideRight; + + float RotateMouseLeft; + float RotateMouseRight; + float RotateMouseUp; + float RotateMouseDown; + float RotateMouse; + + float Action; +} SampleInputsCameraActions; + +typedef struct +{ + SampleVector3 Position; + SampleVector3 PositionVelocity; + SampleVector3 Rotation; + SampleVector3 RotationVelocity; +} SampleCamera; + +typedef struct +{ + SampleCamera Camera; + SampleCamera DebugCamera; + SampleMatrix4x4 ProjectionMatrix; + SampleMatrix4x4 ViewProjMatrix; + float Action; +} SampleInputsCameraState; + +typedef struct +{ + SampleInputsCameraActions InputActions; + SampleInputActionBindingSpan InputActionBindings; + SampleInputsCameraState State; +} SampleInputsCamera; + +// TODO: Allow initial camera setup +void SampleInputsCameraInit(SampleInputsCamera* inputs) +{ + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyA, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyD, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyW, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveForward); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyS, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveBackward); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyLeftArrow, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyRightArrow, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyUpArrow, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyDownArrow, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateDown); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyQ, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyE, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveForward); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadLeftStickYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.MoveBackward); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightStickXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightStickXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightStickYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_GamepadRightStickYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateDown); + + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseRightButton, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateMouse); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateMouseLeft); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisXPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateMouseRight); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYNegative, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateMouseUp); + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_MouseAxisYPositive, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateMouseDown); + + //SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyQ, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideLeft); + //SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeyE, 0, SampleInputActionBindingType_Value, &inputs->InputActions.RotateSideRight); + + // TODO: Remove that one? + SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); + + // TODO: Temporary Init code for now + inputs->State.Camera.Position = (SampleVector3) { 0.0f, 0.0f, -0.0f }; + inputs->State.Camera.Rotation = V3Zero; +} + +SampleVector3 ComputeDeltaAndVelocity(SampleVector3* currentVelocity, SampleVector3 directionVector, float accelerationFactor, float frictionFactor, float deltaTimeInSeconds) +{ + SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(directionVector, accelerationFactor), SampleMulScalarV3(SampleInverseV3(*currentVelocity), frictionFactor)); + + SampleVector3 delta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(*currentVelocity, deltaTimeInSeconds)); + *currentVelocity = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), *currentVelocity); + + return delta; +} + +SampleMatrix4x4 UpdateCamera(SampleCamera* camera, const SampleInputsCameraActions* inputActions, const ElemSwapChainUpdateParameters* updateParameters) +{ + // TODO: Should we normalize the input direction? It feels snappier without it. + + SampleVector3 movementDirection = (SampleVector3) + { + .X = (inputActions->MoveRight - inputActions->MoveLeft), + .Y = 0.0f, + .Z = (inputActions->MoveForward - inputActions->MoveBackward) + }; + + SampleVector3 movementDelta = ComputeDeltaAndVelocity(&camera->PositionVelocity, movementDirection, 15000.0f, 40.0f, updateParameters->DeltaTimeInSeconds); + //printf("Current Pos Vel: %f %f %f\n", camera->PositionVelocity.X, camera->PositionVelocity.Y, camera->PositionVelocity.Z); + + SampleVector3 rotationDirection = (SampleVector3) + { + .X = (inputActions->RotateUp - inputActions->RotateDown), + .Y = (inputActions->RotateLeft - inputActions->RotateRight), + .Z = 0.0f, + }; + + // TODO: Prevent X Rotate above 180 deg + SampleVector3 rotationDelta = ComputeDeltaAndVelocity(&camera->RotationVelocity, rotationDirection, 80.0f, 40.0f, updateParameters->DeltaTimeInSeconds); + + if (SampleMagnitudeSquaredV3(rotationDelta)) + { + camera->Rotation = SampleAddV3(camera->Rotation, rotationDelta); + } + + if (inputActions->RotateMouse) + { + SampleVector3 rotationMouseDelta = (SampleVector3) + { + .X = (inputActions->RotateMouseUp - inputActions->RotateMouseDown), + .Y = (inputActions->RotateMouseLeft - inputActions->RotateMouseRight), + .Z = 0.0f, + }; + + if (SampleMagnitudeSquaredV3(rotationMouseDelta)) + { + camera->Rotation = SampleAddV3(camera->Rotation, SampleMulScalarV3(rotationMouseDelta, 1.0f * updateParameters->DeltaTimeInSeconds)); + } + } + + // TODO: Review this + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, camera->Rotation.X), + SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, camera->Rotation.Z), + SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, camera->Rotation.Y))); + + if (SampleMagnitudeSquaredV3(movementDelta)) + { + SampleMatrix4x4 translationTransform = SampleCreateTransformMatrix(rotationQuaternion, V3Zero); + SampleVector3 rotatedMovementDelta = SampleTransformPointV3(movementDelta, translationTransform); + camera->Position = SampleAddV3(camera->Position, rotatedMovementDelta); + } + + SampleMatrix4x4 targetTransform = SampleCreateTransformMatrix(rotationQuaternion, V3Zero); + SampleVector3 cameraTarget = SampleTransformPointV3((SampleVector3) { 0.0f, 0.0f, 1.0f }, targetTransform); + cameraTarget = SampleAddV3(cameraTarget, camera->Position); + + return SampleCreateLookAtLHMatrix(camera->Position, cameraTarget, (SampleVector3) { 0.0f, 1.0f, 0.0f }); +} + +void SampleInputsCameraUpdate(ElemInputStream inputStream, SampleInputsCamera* inputs, const ElemSwapChainUpdateParameters* updateParameters) +{ + SampleInputsCameraState* state = &inputs->State; + SampleInputsCameraActions* inputActions = &inputs->InputActions; + + SampleUpdateInputActions(&inputs->InputActionBindings, inputStream); + + // TODO: Change speed with a special "run" button + // TODO: Select debug camera when needed + SampleCamera* currentCamera = &state->Camera; + SampleMatrix4x4 viewMatrix = UpdateCamera(currentCamera, inputActions, updateParameters); + + if (updateParameters->SizeChanged || state->ProjectionMatrix.m[2][3] == 0.0f) + { + state->ProjectionMatrix = SampleCreatePerspectiveProjectionMatrix(0.78f, updateParameters->SwapChainInfo.AspectRatio, 0.001f); + } + + state->ViewProjMatrix = SampleMulMatrix4x4(viewMatrix, state->ProjectionMatrix); + state->Action = inputActions->Action; +} diff --git a/samples/Common/SampleModelViewerInputs.h b/samples/Common/SampleInputsModelViewer.h similarity index 94% rename from samples/Common/SampleModelViewerInputs.h rename to samples/Common/SampleInputsModelViewer.h index a886068d..d215bfae 100644 --- a/samples/Common/SampleModelViewerInputs.h +++ b/samples/Common/SampleInputsModelViewer.h @@ -51,7 +51,7 @@ typedef struct float AccelerometerZNegative; float AccelerometerZPositive; -} SampleModelViewerInputActions; +} SampleInputsModelViewerActions; typedef struct { @@ -63,16 +63,16 @@ typedef struct float Zoom; float Action; float InitialAccelerometerZDelta; -} SampleModelViewerInputState; +} SampleInputsModelViewerState; typedef struct { - SampleModelViewerInputActions InputActions; + SampleInputsModelViewerActions InputActions; SampleInputActionBindingSpan InputActionBindings; - SampleModelViewerInputState State; -} SampleModelViewerInputs; + SampleInputsModelViewerState State; +} SampleInputsModelViewer; -void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) +void SampleInputsModelViewerInit(SampleInputsModelViewer* inputs) { // TODO: Review rotation signs @@ -137,18 +137,18 @@ void SampleModelViewerInputsInit(SampleModelViewerInputs* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_Touch, 0, SampleInputActionBindingType_DoubleReleasedSwitch, &inputs->InputActions.Action); } -void SampleModelViewerInputsResetTouchParameters(SampleModelViewerInputState* state) +void SampleInputsModelViewerResetTouchParameters(SampleInputsModelViewerState* state) { state->RotationTouch = V2Zero; state->PreviousTouchDistance = 0.0f; state->PreviousTouchAngle = 0.0f; } -void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewerInputs* inputs, float deltaTimeInSeconds) +void SampleInputsModelViewerUpdate(ElemInputStream inputStream, SampleInputsModelViewer* inputs, float deltaTimeInSeconds) { // TODO: Review rotation order in left handed - SampleModelViewerInputState* state = &inputs->State; - SampleModelViewerInputActions* inputActions = &inputs->InputActions; + SampleInputsModelViewerState* state = &inputs->State; + SampleInputsModelViewerActions* inputActions = &inputs->InputActions; state->RotationDelta = V3Zero; SampleUpdateInputActions(&inputs->InputActionBindings, inputStream); @@ -179,7 +179,7 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe } else { - SampleModelViewerInputsResetTouchParameters(state); + SampleInputsModelViewerResetTouchParameters(state); state->RotationDelta.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; state->RotationDelta.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; @@ -187,13 +187,13 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe } else if (inputActions->TouchRotateSide) { - SampleModelViewerInputsResetTouchParameters(state); + SampleInputsModelViewerResetTouchParameters(state); state->RotationDelta.Z = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; } else if (inputActions->TouchReleased && !inputActions->Touch2) { - SampleModelViewerInputsResetTouchParameters(state); + SampleInputsModelViewerResetTouchParameters(state); state->RotationTouch.X = (inputActions->TouchRotateUp - inputActions->TouchRotateDown) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; state->RotationTouch.Y = (inputActions->TouchRotateLeft - inputActions->TouchRotateRight) * SAMPLE_MODELVIEWER_ROTATION_TOUCH_SPEED * deltaTimeInSeconds; @@ -233,7 +233,7 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe { SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, SAMPLE_MODELVIEWER_ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(state->CurrentRotationSpeed), SAMPLE_MODELVIEWER_ROTATION_FRICTION)); - SampleModelViewerInputsResetTouchParameters(state); + SampleInputsModelViewerResetTouchParameters(state); state->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(state->CurrentRotationSpeed, deltaTimeInSeconds)); state->CurrentRotationSpeed = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), state->CurrentRotationSpeed); } @@ -253,7 +253,7 @@ void SampleModelViewerInputsUpdate(ElemInputStream inputStream, SampleModelViewe if (SampleMagnitudeV2(state->RotationTouch) < 0.001f) { - SampleModelViewerInputsResetTouchParameters(state); + SampleInputsModelViewerResetTouchParameters(state); } } diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 247e73c3..30ce9a0e 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -11,7 +11,7 @@ // TODO: Review the angle functions, we are using left handed coordinate system. So positive rotation should // be in clockwise order -float SamplePow2f(float value) +inline float SamplePow2f(float value) { return value * value; } @@ -159,6 +159,17 @@ SampleVector3 SampleAddV3(SampleVector3 v1, SampleVector3 v2) return result; } +SampleVector3 SampleMinV3(SampleVector3 v1, SampleVector3 v2) +{ + SampleVector3 result; + + result.X = v1.X - v2.X; + result.Y = v1.Y - v2.Y; + result.Z = v1.Z - v2.Z; + + return result; +} + SampleVector3 SampleMulScalarV3(SampleVector3 v, float scalar) { SampleVector3 result; @@ -257,15 +268,15 @@ SampleVector4 SampleMulQuat(SampleVector4 q1, SampleVector4 q2) return (SampleVector4) { .X = x, .Y = y, .Z = z, .W = w }; } -// TODO: Get rid of the 4x4 here and write a function that convert it to constant buffer format -typedef struct +typedef union { float m[4][4]; -} SampleMatrix3x3; + SampleVector4 Rows[4]; +} SampleMatrix4x4; -SampleMatrix3x3 SampleCreateIdentityMatrix() +SampleMatrix4x4 SampleCreateIdentityMatrix() { - SampleMatrix3x3 result; + SampleMatrix4x4 result; result.m[0][0] = 1.0f; result.m[0][1] = 0.0f; @@ -282,12 +293,60 @@ SampleMatrix3x3 SampleCreateIdentityMatrix() result.m[2][2] = 1.0f; result.m[2][3] = 0.0f; + result.m[3][0] = 0.0f; + result.m[3][1] = 0.0f; + result.m[3][2] = 0.0f; + result.m[3][3] = 1.0f; + + return result; +} + +SampleMatrix4x4 SampleCreateTransformMatrix(SampleVector4 quaternion, SampleVector3 translation) +{ + float xw = quaternion.X *quaternion.W , xx = quaternion.X *quaternion.X , yy = quaternion.Y *quaternion.Y , + yw = quaternion.Y *quaternion.W , xy = quaternion.X *quaternion.Y , yz = quaternion.Y *quaternion.Z , + zw = quaternion.Z *quaternion.W , xz = quaternion.X *quaternion.Z , zz = quaternion.Z *quaternion.Z ; + + SampleMatrix4x4 result; + + result.Rows[0] = (SampleVector4) { .X = 1-2*(yy+zz), .Y = 2*(xy+zw), .Z = 2*(xz-yw), .W = 0.0f }; + result.Rows[1] = (SampleVector4) { .X = 2*(xy-zw), .Y = 1-2*(xx+zz), .Z = 2*(yz+xw), .W = 0.0f }; + result.Rows[2] = (SampleVector4) { .X = 2*(xz+yw), .Y = 2*(yz-xw), .Z = 1-2*(xx+yy), .W = 0.0f }; + result.Rows[3] = (SampleVector4) { .X = 0.0f, .Y = 0.0f, .Z = 0.0f, .W = 1.0f }; + + return result; +} + +SampleMatrix4x4 SampleTransposeMatrix(SampleMatrix4x4 matrix) +{ + SampleMatrix4x4 result; + + result.m[0][0] = matrix.m[0][0]; + result.m[0][1] = matrix.m[1][0]; + result.m[0][2] = matrix.m[2][0]; + result.m[0][3] = matrix.m[3][0]; + + result.m[1][0] = matrix.m[0][1]; + result.m[1][1] = matrix.m[1][1]; + result.m[1][2] = matrix.m[2][1]; + result.m[1][3] = matrix.m[3][1]; + + result.m[2][0] = matrix.m[0][2]; + result.m[2][1] = matrix.m[1][2]; + result.m[2][2] = matrix.m[2][2]; + result.m[2][3] = matrix.m[3][2]; + + result.m[3][0] = matrix.m[0][3]; + result.m[3][1] = matrix.m[1][3]; + result.m[3][2] = matrix.m[2][3]; + result.m[3][3] = matrix.m[3][3]; + return result; } -SampleMatrix3x3 SampleCreateRotationMatrix(float angle) +SampleMatrix4x4 SampleCreateRotationMatrix(float angle) { - SampleMatrix3x3 result; + SampleMatrix4x4 result; float c = cosf(angle); float s = sinf(angle); @@ -303,12 +362,14 @@ SampleMatrix3x3 SampleCreateRotationMatrix(float angle) result.m[2][1] = 0.0f; result.m[2][2] = 1.0f; + result.m[3][3] = 1.0f; + return result; } -SampleMatrix3x3 SampleCreateScaleMatrix(float scale) +SampleMatrix4x4 SampleCreateScaleMatrix(float scale) { - SampleMatrix3x3 result; + SampleMatrix4x4 result; result.m[0][0] = scale; result.m[0][1] = 0.0f; @@ -321,13 +382,15 @@ SampleMatrix3x3 SampleCreateScaleMatrix(float scale) result.m[2][0] = 0.0f; result.m[2][1] = 0.0f; result.m[2][2] = 1.0f; + + result.m[3][3] = 1.0f; return result; } -SampleMatrix3x3 SampleCreateTranslationMatrix(float tx, float ty) +SampleMatrix4x4 SampleCreateTranslationMatrix(float tx, float ty) { - SampleMatrix3x3 result; + SampleMatrix4x4 result; result.m[0][0] = 1.0f; result.m[0][1] = 0.0f; @@ -340,26 +403,58 @@ SampleMatrix3x3 SampleCreateTranslationMatrix(float tx, float ty) result.m[2][0] = 0.0f; result.m[2][1] = 0.0f; result.m[2][2] = 1.0f; + + result.m[3][3] = 1.0f; return result; } -SampleMatrix3x3 SampleMulMatrix3x3(SampleMatrix3x3 a, SampleMatrix3x3 b) +SampleMatrix4x4 SampleCreateLookAtLHMatrix(SampleVector3 eyePosition, SampleVector3 targetPosition, SampleVector3 upDirection) { - SampleMatrix3x3 result; + SampleVector3 forwardDirection = SampleNormalizeV3(SampleMinV3(targetPosition, eyePosition)); + SampleVector3 rightDirection = SampleNormalizeV3(SampleCrossProductV3(upDirection, forwardDirection)); + SampleVector3 upDirectionNew = SampleCrossProductV3(forwardDirection, rightDirection); + + SampleMatrix4x4 result; - for (int i = 0; i < 3; i++) + result.Rows[0] = (SampleVector4) { .X = rightDirection.X, .Y = upDirectionNew.X, .Z = forwardDirection.X, .W = 0.0f }; + result.Rows[1] = (SampleVector4) { .X = rightDirection.Y, .Y = upDirectionNew.Y, .Z = forwardDirection.Y, .W = 0.0f }; + result.Rows[2] = (SampleVector4) { .X = rightDirection.Z, .Y = upDirectionNew.Z, .Z = forwardDirection.Z, .W = 0.0f }; + result.Rows[3] = (SampleVector4) { .X = -SampleDotProductV3(rightDirection, eyePosition), .Y = -SampleDotProductV3(upDirectionNew, eyePosition), .Z = -SampleDotProductV3(forwardDirection, eyePosition), .W = 1.0f }; + + return result; +} + +SampleMatrix4x4 SampleCreatePerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) +{ + float height = 1.0f / tanf(fovY * 0.5f); + + SampleMatrix4x4 result; + + result.Rows[0] = (SampleVector4) { .X = height / aspectRatio, .Y = 0.0f, .Z = 0.0f, .W = 0.0f }; + result.Rows[1] = (SampleVector4) { .X = 0.0f, .Y = height, .Z = 0.0f, .W = 0.0f }; + result.Rows[2] = (SampleVector4) { .X = 0.0f, .Y = 0.0f, .Z = 0.0f, .W = 1.0f }; + result.Rows[3] = (SampleVector4) { .X = 0.0f, .Y = 0.0f, .Z = zNear, .W = 0.0f }; + + return result; +} + +SampleMatrix4x4 SampleMulMatrix4x4(SampleMatrix4x4 a, SampleMatrix4x4 b) +{ + SampleMatrix4x4 result; + + for (int i = 0; i < 4; i++) { - for (int j = 0; j < 3; j++) + for (int j = 0; j < 4; j++) { - result.m[i][j] = a.m[i][0] * b.m[0][j] + a.m[i][1] * b.m[1][j] + a.m[i][2] * b.m[2][j]; + result.m[i][j] = a.m[i][0] * b.m[0][j] + a.m[i][1] * b.m[1][j] + a.m[i][2] * b.m[2][j] + a.m[i][3] * b.m[3][j]; } } return result; } -SampleVector2 SampleTransformPoint(SampleVector2 point, SampleMatrix3x3 m) +SampleVector2 SampleTransformPoint(SampleVector2 point, SampleMatrix4x4 m) { SampleVector2 result; @@ -368,3 +463,18 @@ SampleVector2 SampleTransformPoint(SampleVector2 point, SampleMatrix3x3 m) return result; } + +SampleVector3 SampleTransformPointV3(SampleVector3 point, SampleMatrix4x4 m) +{ + SampleVector3 result; + + result.X = m.m[0][0] * point.X + m.m[0][1] * point.Y + m.m[0][2] * point.Z + m.m[0][3]; + result.Y = m.m[1][0] * point.X + m.m[1][1] * point.Y + m.m[1][2] * point.Z + m.m[1][3]; + result.Z = m.m[2][0] * point.X + m.m[2][1] * point.Y + m.m[2][2] * point.Z + m.m[2][3]; + + //result.X = m.m[0][0] * point.X + m.m[1][0] * point.Y + m.m[2][0] * point.Z + m.m[3][0]; + //result.Y = m.m[0][1] * point.X + m.m[1][1] * point.Y + m.m[2][1] * point.Z + m.m[3][1]; + //result.Z = m.m[0][2] * point.X + m.m[1][2] * point.Y + m.m[2][2] * point.Z + m.m[3][2]; + + return result; +} diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index 051d0c1c..b4278644 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -18,7 +18,7 @@ typedef struct uint32_t RenderTextureIndex; float Zoom; uint32_t Padding[2]; - SampleMatrix3x3 Transform; + SampleMatrix4x4 Transform; } ShaderParameters; typedef struct @@ -282,9 +282,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void UpdateGameState(gameState, inputActions, updateParameters->DeltaTimeInSeconds); - SampleMatrix3x3 transformMatrix = SampleMulMatrix3x3(SampleCreateTranslationMatrix(gameState->TranslationDelta.X, gameState->TranslationDelta.Y), SampleCreateRotationMatrix(gameState->RotationDelta)); + SampleMatrix4x4 transformMatrix = SampleMulMatrix4x4(SampleCreateTranslationMatrix(gameState->TranslationDelta.X, gameState->TranslationDelta.Y), SampleCreateRotationMatrix(gameState->RotationDelta)); - applicationPayload->ShaderParameters.Transform = SampleMulMatrix3x3(applicationPayload->ShaderParameters.Transform, transformMatrix); + applicationPayload->ShaderParameters.Transform = SampleMulMatrix4x4(applicationPayload->ShaderParameters.Transform, transformMatrix); applicationPayload->ShaderParameters.Zoom = gameState->Zoom; // TODO: Do an example with another compute queue to demonstrate fences? diff --git a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl index 27f397b6..d616489e 100644 --- a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl @@ -22,6 +22,8 @@ struct ElemMeshlet uint32_t TriangleCount; }; +// NOTE: The vertex layout is not optimized to not add complexity to the sample. +// Do not use this on production code. struct Vertex { float3 Position; diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index bc022b00..bb7470fd 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -1,8 +1,8 @@ #include "Elemental.h" #include "SampleUtils.h" #include "SampleMath.h" -#include "SampleApplicationInputs.h" -#include "SampleModelViewerInputs.h" +#include "SampleInputsApplication.h" +#include "SampleInputsModelViewer.h" #include "SampleMesh.h" typedef struct @@ -46,8 +46,8 @@ typedef struct ElemGraphicsResource DepthBuffer; ElemPipelineState GraphicsPipeline; ShaderParameters ShaderParameters; - SampleApplicationInputs ApplicationInputs; - SampleModelViewerInputs ModelViewerInputs; + SampleInputsApplication InputsApplication; + SampleInputsModelViewer InputsModelViewer; MeshData TestMeshData; } ApplicationPayload; @@ -178,13 +178,13 @@ void InitSample(void* payload) applicationPayload->ShaderParameters.RotationQuaternion = (SampleVector4){ .X = 0, .Y = 0, .Z = 0, .W = 1 }; - SampleApplicationInputsInit(&applicationPayload->ApplicationInputs); - SampleModelViewerInputsInit(&applicationPayload->ModelViewerInputs); + SampleInputsApplicationInit(&applicationPayload->InputsApplication); + SampleInputsModelViewerInit(&applicationPayload->InputsModelViewer); if (applicationPayload->AppSettings.PreferFullScreen) { ElemHideWindowCursor(applicationPayload->Window); - applicationPayload->ApplicationInputs.State.IsCursorDisplayed = false; + applicationPayload->InputsApplication.State.IsCursorDisplayed = false; } SampleStartFrameMeasurement(); @@ -220,26 +220,26 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemInputStream inputStream = ElemGetInputStream(); - SampleApplicationInputsUpdate(inputStream, &applicationPayload->ApplicationInputs, updateParameters->DeltaTimeInSeconds); - SampleModelViewerInputsUpdate(inputStream, &applicationPayload->ModelViewerInputs, updateParameters->DeltaTimeInSeconds); + SampleInputsApplicationUpdate(inputStream, &applicationPayload->InputsApplication, updateParameters->DeltaTimeInSeconds); + SampleInputsModelViewerUpdate(inputStream, &applicationPayload->InputsModelViewer, updateParameters->DeltaTimeInSeconds); - if (applicationPayload->ApplicationInputs.State.ExitApplication) + if (applicationPayload->InputsApplication.State.ExitApplication) { ElemExitApplication(0); } - if (applicationPayload->ApplicationInputs.State.ShowCursor) + if (applicationPayload->InputsApplication.State.ShowCursor) { printf("Show cursor\n"); ElemShowWindowCursor(applicationPayload->Window); } - else if (applicationPayload->ApplicationInputs.State.HideCursor) + else if (applicationPayload->InputsApplication.State.HideCursor) { printf("Hide cursor\n"); ElemHideWindowCursor(applicationPayload->Window); } - SampleModelViewerInputState* modelViewerState = &applicationPayload->ModelViewerInputs.State; + SampleInputsModelViewerState* modelViewerState = &applicationPayload->InputsModelViewer.State; if (SampleMagnitudeSquaredV3(modelViewerState->RotationDelta)) { diff --git a/samples/Elemental/99-Renderer/CMakeLists.txt b/samples/Elemental/99-Renderer/CMakeLists.txt new file mode 100644 index 00000000..3ec9ef6f --- /dev/null +++ b/samples/Elemental/99-Renderer/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SAMPLE_NAME Renderer) + +# TODO: Download assets etc. + +add_executable(${SAMPLE_NAME} main.c Data) +target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) +target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) +add_dependencies(${SAMPLE_NAME} SampleSharedData) + +configure_resource_compilation(${SAMPLE_NAME} resource_list) +configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl new file mode 100644 index 00000000..e70f692e --- /dev/null +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -0,0 +1,130 @@ +struct ShaderParameters +{ + uint32_t FrameDataBufferIndex; + uint32_t VertexBufferIndex; + uint32_t MeshletBufferIndex; + uint32_t MeshletVertexIndexBufferIndex; + uint32_t MeshletTriangleIndexBufferIndex; + uint32_t ShowMeshlets; + uint32_t MeshletCount; +}; + +[[vk::push_constant]] +ShaderParameters parameters : register(b0); + +struct FrameData +{ + float4x4 ViewProjMatrix; +}; + +struct ElemMeshlet +{ + uint32_t VertexIndexOffset; + uint32_t VertexIndexCount; + uint32_t TriangleOffset; + uint32_t TriangleCount; +}; + +struct Vertex +{ + float3 Position; + float3 Normal; + float2 TextureCoordinates; +}; + +struct VertexOutput +{ + float4 Position: SV_Position; + float3 WorldNormal: NORMAL0; + uint MeshletIndex : COLOR0; +}; +#define IDENTITY_MATRIX float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) +// TODO: Put that in an include file +float4x4 TransformMatrix(float4 quaternion, float3 translation) +{ + float xw = quaternion.x*quaternion.w, xx = quaternion.x*quaternion.x, yy = quaternion.y*quaternion.y, + yw = quaternion.y*quaternion.w, xy = quaternion.x*quaternion.y, yz = quaternion.y*quaternion.z, + zw = quaternion.z*quaternion.w, xz = quaternion.x*quaternion.z, zz = quaternion.z*quaternion.z; + + float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); + float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); + float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); + float4 row4 = float4(0.0, 0.0, 0.0, 1.0); + + return float4x4(row1, row2, row3, row4); +} + +[shader("mesh")] +[OutputTopology("triangle")] +[NumThreads(126, 1, 1)] +void MeshMain(in uint groupId: SV_GroupID, + in uint groupThreadId : SV_GroupThreadID, + out vertices VertexOutput vertices[64], + out indices uint3 indices[126]) +{ + uint meshletIndex = groupId; + + ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; + ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); + + SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); + + if (groupThreadId < meshlet.VertexIndexCount) + { + ByteAddressBuffer frameDataBuffer = ResourceDescriptorHeap[parameters.FrameDataBufferIndex]; + FrameData frameData = frameDataBuffer.Load(0); + + //float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); + float4x4 worldMatrix = IDENTITY_MATRIX; + + float4x4 inverseTransposeWorldMatrix = worldMatrix; + + ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; + ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; + + uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); + Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); + + //vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), mul(worldMatrix, frameData.ViewProjMatrix)); + // NOTE: This calculation is faster because v * M is faster than M * M + vertices[groupThreadId].Position = mul(mul(float4(vertex.Position, 1.0), worldMatrix), frameData.ViewProjMatrix); + vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].MeshletIndex = groupId; + } + + if (groupThreadId < meshlet.TriangleCount) + { + ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; + uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); + + indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; + } +} + +uint hash(uint a) +{ + a = (a+0x7ed55d16) + (a<<12); + a = (a^0xc761c23c) ^ (a>>19); + a = (a+0x165667b1) + (a<<5); + a = (a+0xd3a2646c) ^ (a<<9); + a = (a+0xfd7046c5) + (a<<3); + a = (a^0xb55a4f09) ^ (a>>16); + + return a; +} + +[shader("pixel")] +float4 PixelMain(const VertexOutput input) : SV_Target0 +{ + if (parameters.ShowMeshlets == 0) + { + return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); + } + else + { + uint hashResult = hash(input.MeshletIndex); + float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; + + return float4(meshletColor, 1.0); + } +} diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c new file mode 100644 index 00000000..61221330 --- /dev/null +++ b/samples/Elemental/99-Renderer/main.c @@ -0,0 +1,326 @@ +#include "Elemental.h" +#include "SampleUtils.h" +#include "SampleMath.h" +#include "SampleInputsApplication.h" +#include "SampleInputsCamera.h" +#include "SampleMesh.h" + +// TODO: Share data between shader and C code + +typedef struct +{ + uint32_t FrameDataBuffer; + uint32_t VertexBuffer; + uint32_t MeshletBuffer; + uint32_t MeshletVertexIndexBuffer; + uint32_t MeshletTriangleIndexBuffer; + uint32_t ShowMeshlets; + uint32_t MeshletCount; +} ShaderParameters; + +typedef struct +{ + SampleMatrix4x4 ViewProjMatrix; +} ShaderFrameData; + +typedef struct +{ + uint32_t MeshletCount; + ElemGraphicsResource VertexBuffer; + ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; + ElemGraphicsResource MeshletBuffer; + ElemGraphicsResourceDescriptor MeshletBufferReadDescriptor; + ElemGraphicsResource MeshletVertexIndexBuffer; + ElemGraphicsResourceDescriptor MeshletVertexIndexBufferReadDescriptor; + ElemGraphicsResource MeshletTriangleIndexBuffer; + ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; +} MeshData; + +// TODO: Group common variables into separate structs +typedef struct +{ + SampleAppSettings AppSettings; + ElemWindow Window; + ElemGraphicsDevice GraphicsDevice; + ElemCommandQueue CommandQueue; + ElemGraphicsHeap GraphicsHeap; + uint32_t CurrentHeapOffset; + ElemFence LastExecutionFence; + ElemSwapChain SwapChain; + ElemGraphicsHeap DepthBufferHeap; + ElemGraphicsResource DepthBuffer; + ElemPipelineState GraphicsPipeline; + ShaderParameters ShaderParameters; + SampleInputsApplication InputsApplication; + SampleInputsCamera InputsCamera; + MeshData TestMeshData; + + ShaderFrameData FrameData; + ElemGraphicsResource FrameDataBuffer; + ElemGraphicsResourceDescriptor FrameDataBufferReadDescriptor; +} ApplicationPayload; + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); + +void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, uint32_t height) +{ + if (applicationPayload->DepthBuffer != ELEM_HANDLE_NULL) + { + ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); + } + + printf("Creating DepthBuffer...\n"); + + ElemGraphicsResourceInfo resourceInfo = ElemCreateTexture2DResourceInfo(applicationPayload->GraphicsDevice, width, height, 1, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil, + &(ElemGraphicsResourceInfoOptions) { + .DebugName = "DepthBuffer" + }); + + applicationPayload->DepthBuffer = ElemCreateGraphicsResource(applicationPayload->DepthBufferHeap, 0, &resourceInfo); +} + +// TODO: To remove when IOQueues +void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, void* dataPointer, uint32_t sizeInBytes) +{ + // TODO: Alignment should be used with the offset before adding the size of the resource! + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); + + applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); + *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); + applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; + + *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(*buffer); + memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); +} + +void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) +{ + // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! + // Add a parameter to specify the length we want to read and the offset + ElemDataSpan meshFileData = SampleReadFile(path); + + uint8_t* fileDataPointer = meshFileData.Items; + SampleMeshHeader* meshHeader = (SampleMeshHeader*)fileDataPointer; + + if (meshHeader->FileId[0] == 'M' && meshHeader->FileId[1] == 'E' && meshHeader->FileId[2] == 'S' && meshHeader->FileId[3] == 'H') + { + printf("OK Meshlet Count: %d\n", meshHeader->MeshletCount); + } + + meshData->MeshletCount = meshHeader->MeshletCount; + + CreateAndUploadDataTemp(&meshData->VertexBuffer, &meshData->VertexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->VertexBufferOffset, meshHeader->VertexBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletBuffer, &meshData->MeshletBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletBufferOffset, meshHeader->MeshletBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletVertexIndexBuffer, &meshData->MeshletVertexIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletVertexIndexBufferOffset, meshHeader->MeshletVertexIndexBufferSizeInBytes); + CreateAndUploadDataTemp(&meshData->MeshletTriangleIndexBuffer, &meshData->MeshletTriangleIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletTriangleIndexBufferOffset, meshHeader->MeshletTriangleIndexBufferSizeInBytes); + + applicationPayload->ShaderParameters.VertexBuffer = meshData->VertexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletBuffer = meshData->MeshletBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = meshData->MeshletVertexIndexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = meshData->MeshletTriangleIndexBufferReadDescriptor; + applicationPayload->ShaderParameters.MeshletCount = meshData->MeshletCount; +} + +void FreeMesh(MeshData* meshData) +{ + ElemFreeGraphicsResourceDescriptor(meshData->VertexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->VertexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletVertexIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletVertexIndexBuffer, NULL); + ElemFreeGraphicsResourceDescriptor(meshData->MeshletTriangleIndexBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(meshData->MeshletTriangleIndexBuffer, NULL); +} + +void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 viewProjMatrix) +{ + applicationPayload->FrameData.ViewProjMatrix = viewProjMatrix; + + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->FrameDataBuffer); + memcpy(vertexBufferPointer.Items, &applicationPayload->FrameData, sizeof(ShaderFrameData)); +} + +void InitSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + applicationPayload->Window = ElemCreateWindow(&(ElemWindowOptions) { .WindowState = applicationPayload->AppSettings.PreferFullScreen ? ElemWindowState_FullScreen : ElemWindowState_Normal }); + + ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); + + // TODO: Debug why the AMD integrated GPU is not create at all + ElemGraphicsDeviceInfoSpan devices = ElemGetAvailableGraphicsDevices(); + + for (uint32_t i = 0; i < devices.Length; i++) + { + printf("Device: %s\n", devices.Items[i].DeviceName); + } + + applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); + + applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); + applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); + ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); + + // TODO: For now we create a separate heap to avoid memory management + applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); + + // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues + // TODO: Having GPU Upload is still annoying 😞 + applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + + CreateAndUploadDataTemp(&applicationPayload->FrameDataBuffer, &applicationPayload->FrameDataBufferReadDescriptor, applicationPayload, &applicationPayload->FrameData, sizeof(ShaderFrameData)); + applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBufferReadDescriptor; + + CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); + LoadMesh(&applicationPayload->TestMeshData, "sponza.mesh", applicationPayload); + + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); + + applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { + .DebugName = "RenderMesh PSO", + .ShaderLibrary = shaderLibrary, + .MeshShaderFunction = "MeshMain", + .PixelShaderFunction = "PixelMain", + .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, + .DepthStencil = + { + .Format = ElemGraphicsFormat_D32_FLOAT, + .DepthCompareFunction = ElemGraphicsCompareFunction_Greater + } + }); + + ElemFreeShaderLibrary(shaderLibrary); + + SampleInputsApplicationInit(&applicationPayload->InputsApplication); + SampleInputsCameraInit(&applicationPayload->InputsCamera); + + if (applicationPayload->AppSettings.PreferFullScreen) + { + ElemHideWindowCursor(applicationPayload->Window); + applicationPayload->InputsApplication.State.IsCursorDisplayed = false; + } + + SampleStartFrameMeasurement(); +} + +void FreeSample(void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); + + FreeMesh(&applicationPayload->TestMeshData); + + ElemFreeGraphicsResourceDescriptor(applicationPayload->FrameDataBufferReadDescriptor, NULL); + ElemFreeGraphicsResource(applicationPayload->FrameDataBuffer, NULL); + + ElemFreePipelineState(applicationPayload->GraphicsPipeline); + ElemFreeSwapChain(applicationPayload->SwapChain); + ElemFreeCommandQueue(applicationPayload->CommandQueue); + + ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); + ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); + + ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); + ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); +} + +void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) +{ + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; + + if (updateParameters->SizeChanged) + { + CreateDepthBuffer(applicationPayload, updateParameters->SwapChainInfo.Width, updateParameters->SwapChainInfo.Height); + } + + ElemInputStream inputStream = ElemGetInputStream(); + + SampleInputsApplicationUpdate(inputStream, &applicationPayload->InputsApplication, updateParameters->DeltaTimeInSeconds); + + // TODO: Use another type of camera + SampleInputsCameraUpdate(inputStream, &applicationPayload->InputsCamera, updateParameters); + + if (applicationPayload->InputsApplication.State.ExitApplication) + { + ElemExitApplication(0); + } + + if (applicationPayload->InputsApplication.State.ShowCursor) + { + printf("Show cursor\n"); + ElemShowWindowCursor(applicationPayload->Window); + } + else if (applicationPayload->InputsApplication.State.HideCursor) + { + printf("Hide cursor\n"); + ElemHideWindowCursor(applicationPayload->Window); + } + + SampleInputsCameraState* inputsCameraState = &applicationPayload->InputsCamera.State; + + applicationPayload->ShaderParameters.ShowMeshlets = inputsCameraState->Action; + + UpdateFrameData(applicationPayload, inputsCameraState->ViewProjMatrix); + + ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + + ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) { + .RenderTargets = + { + .Items = (ElemRenderPassRenderTarget[]) { + { + .RenderTarget = updateParameters->BackBufferRenderTarget, + .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, + }}, + .Length = 1 + }, + .DepthStencil = + { + .DepthStencil = applicationPayload->DepthBuffer + } + }); + + ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); + ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + + ElemEndRenderPass(commandList); + + ElemCommitCommandList(commandList); + applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); + + ElemPresentSwapChain(applicationPayload->SwapChain); + SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); + + if (frameMeasurement.HasNewData) + { + SampleSetWindowTitle(applicationPayload->Window, "Renderer", applicationPayload->GraphicsDevice, frameMeasurement.FrameTimeInSeconds, frameMeasurement.Fps); + } + + SampleStartFrameMeasurement(); +} + +int main(int argc, const char* argv[]) +{ + ApplicationPayload payload = + { + .AppSettings = SampleParseAppSettings(argc, argv) + }; + + // TODO: Load and save state + + ElemConfigureLogHandler(ElemConsoleLogHandler); + + ElemRunApplication(&(ElemRunApplicationParameters) + { + .ApplicationName = "Renderer", + .InitHandler = InitSample, + .FreeHandler = FreeSample, + .Payload = &payload + }); +} diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-MeshCompiler/main.c index c09122c0..529d793d 100644 --- a/samples/ElementalTools/02-MeshCompiler/main.c +++ b/samples/ElementalTools/02-MeshCompiler/main.c @@ -2,12 +2,14 @@ #include "SampleUtils.h" #include "SampleMesh.h" +// TODO: Rename this to scene compiler + int main(int argc, const char* argv[]) { // TODO: Add an option to handle handness change if (argc < 3) { - printf("USAGE: MeshCompiler [options] inputfile outputfile\n"); + printf("USAGE: SceneCompiler [options] inputfile outputfile\n"); printf("\n"); printf("OPTIONS:\n"); printf(" --meshlet-triangle-count\tTBD: TBD. Default: TBD.\n"); @@ -62,10 +64,10 @@ int main(int argc, const char* argv[]) return 1; } - ElemLoadMeshResult inputMesh = ElemLoadMesh(inputPath, &(ElemLoadMeshOptions) { .MeshCoordinateSystem = ElemMeshCoordinateSystem_LeftHanded }); - printf("Input mesh vertex Count: %d\n", inputMesh.VertexCount); + ElemLoadSceneResult inputScene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .SceneCoordinateSystem = ElemSceneCoordinateSystem_LeftHanded }); + printf("Input mesh vertex Count: %d\n", inputScene.VertexCount); - ElemBuildMeshletResult result = ElemBuildMeshlets(inputMesh.VertexBuffer, NULL); + ElemBuildMeshletResult result = ElemBuildMeshlets(inputScene.VertexBuffer, NULL); // TODO: Refactor this into an util function to display messages with proper colors for (uint32_t i = 0; i < result.Messages.Length; i++) @@ -112,7 +114,7 @@ int main(int argc, const char* argv[]) } } - printf("Writing mesh data to: %s\n", outputPath); + printf("Writing Scene data to: %s\n", outputPath); uint32_t meshletBufferSize = result.Meshlets.Length * sizeof(ElemMeshlet); uint32_t meshletVertexIndexBufferSize = result.MeshletVertexIndexBuffer.Length * sizeof(uint32_t); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 2eab1b0a..2430bd89 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -42,7 +42,7 @@ typedef uint64_t ElemHandle; #define ELEM_HANDLE_NULL 0u //-------------------------------------------------------------------------------- -// ##Module_Application## +// Module: Elemental //-------------------------------------------------------------------------------- /** diff --git a/src/Elemental/Microsoft/Win32Inputs.cpp b/src/Elemental/Microsoft/Win32Inputs.cpp index 593c9572..05fcaab8 100644 --- a/src/Elemental/Microsoft/Win32Inputs.cpp +++ b/src/Elemental/Microsoft/Win32Inputs.cpp @@ -39,6 +39,7 @@ ElemInputDevice AddWin32RawInputDevice(HANDLE device, DWORD type) } else if (type == RIM_TYPEHID) { + // BUG: Crash when steam input is activated if (!IsHidDeviceSupported(rawInputDeviceInfo->hid.dwVendorId, rawInputDeviceInfo->hid.dwProductId)) { SystemLogWarningMessage(ElemLogMessageCategory_Inputs, "HID Input device is not supported. (Vendor: %d, Product: %d)", rawInputDeviceInfo->hid.dwVendorId, rawInputDeviceInfo->hid.dwProductId); diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Common/DirectXShaderCompiler.cpp index d7ee2745..64d94bbb 100644 --- a/src/ElementalTools/Common/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Common/DirectXShaderCompiler.cpp @@ -135,6 +135,9 @@ ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn parameters[parameterIndex++] = DXC_ARG_ALL_RESOURCES_BOUND; + // TODO: Add an options for this + parameters[parameterIndex++] = DXC_ARG_PACK_MATRIX_ROW_MAJOR; + if (options && options->DebugMode) { parameters[parameterIndex++] = DXC_ARG_DEBUG; diff --git a/src/ElementalTools/Common/MeshLoader.cpp b/src/ElementalTools/Common/MeshLoader.cpp index 51386c1d..c0a240c0 100644 --- a/src/ElementalTools/Common/MeshLoader.cpp +++ b/src/ElementalTools/Common/MeshLoader.cpp @@ -4,7 +4,7 @@ #include "SystemFunctions.h" // TODO: Do one for each thread -static MemoryArena MeshLoaderMemoryArena; +static MemoryArena SceneLoaderMemoryArena; struct ObjLoaderFileData { @@ -44,7 +44,6 @@ size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userDa if (readLength > 0) { - //printf("ReadObj: %d/%d\n", (uint32_t)bytes, (uint32_t)readLength); SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); } @@ -60,7 +59,7 @@ unsigned long FastObjFileSize(void* file, void* userData) return objLoaderFileData->FileData.Length; } -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemMeshCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) { // TODO: Check what to include auto currentVertexBufferPointer = *vertexBufferPointer; @@ -72,7 +71,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemMe ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; } - if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; } @@ -87,7 +86,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemMe ((float*)currentVertexBufferPointer)[j] = objMesh->normals[3 * objVertex.n + j]; } - if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; } @@ -108,18 +107,30 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemMe *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ElemLoadMeshOptions* options) +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ElemLoadSceneOptions* options) { + for (uint32_t i = 0; i < objMesh->object_count; i++) + { + auto object = objMesh->objects[i]; + printf("Object (Node): %s\n", object.name); + + /* + for (uint32_t j = 0; j < object.face_count; j++) + { + auto face = objMesh->face_materials + auto group = objMesh->face_materials[j]; + printf("Group: %s\n", group.name); + }*/ + } + // TODO: Handle sub objects - ElemMeshCoordinateSystem coordinateSystem = ElemMeshCoordinateSystem_LeftHanded; + ElemSceneCoordinateSystem coordinateSystem = ElemSceneCoordinateSystem_LeftHanded; if (options != nullptr) { - coordinateSystem = options->MeshCoordinateSystem; + coordinateSystem = options->SceneCoordinateSystem; } - printf("Coord: %d\n", coordinateSystem); - auto positionSize = sizeof(float) * 3; auto normalSize = sizeof(float) * 3; auto textureCoordinatesSize = sizeof(float) * 2; @@ -135,7 +146,7 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj for (uint32_t i = 0; i < objMesh->index_count; i += 3) { - if (coordinateSystem == ElemMeshCoordinateSystem_LeftHanded) + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); @@ -156,20 +167,20 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj }; } -void InitMeshLoaderMemoryArena() +void InitSceneLoaderMemoryArena() { - if (MeshLoaderMemoryArena.Storage == nullptr) + if (SceneLoaderMemoryArena.Storage == nullptr) { - MeshLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + SceneLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); } - SystemClearMemoryArena(MeshLoaderMemoryArena); + SystemClearMemoryArena(SceneLoaderMemoryArena); } // TODO: Split object loaders into multiple files -ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(const char* path, const ElemLoadMeshOptions* options) +ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadSceneOptions* options) { - InitMeshLoaderMemoryArena(); + InitSceneLoaderMemoryArena(); // TODO: To output the results, we should use a separate memory arena for each modules @@ -186,7 +197,8 @@ ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(const char* path, const ElemLoadMes auto mesh = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); - auto vertexBuffer = ConstructObjVertexBuffer(MeshLoaderMemoryArena, mesh, options); + // TODO: Support instances and meshes + auto vertexBuffer = ConstructObjVertexBuffer(SceneLoaderMemoryArena, mesh, options); return { diff --git a/src/ElementalTools/Common/UnityBuild.cpp b/src/ElementalTools/Common/UnityBuild.cpp index 52b53d60..6efd8f1d 100644 --- a/src/ElementalTools/Common/UnityBuild.cpp +++ b/src/ElementalTools/Common/UnityBuild.cpp @@ -7,8 +7,7 @@ #include "MetalShaderConverter.cpp" #endif -#define FAST_OBJ_IMPLEMENTATION -#include "fast_obj.h" +#include "fast_obj.c" #include "MeshLoader.cpp" #include "MeshletBuilder.cpp" diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 9bc8463f..a9863420 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -37,7 +37,7 @@ #endif //------------------------------------------------------------------------ -// ##Module_Tools## +// Module: Elemental Tools //------------------------------------------------------------------------ /** @@ -92,6 +92,12 @@ typedef struct uint32_t Length; } ElemToolsDataSpan; +typedef struct +{ + uint32_t* Items; + uint32_t Length; +} ElemUInt32Span; + /** * Represents a message produced by the tools, including a type and text. */ @@ -114,13 +120,20 @@ typedef struct uint32_t Length; } ElemToolsMessageSpan; +typedef struct +{ + ElemToolsDataSpan Data; + uint32_t VertexSize; + // TODO: Add vertex description structure +} ElemVertexBuffer; + typedef ElemToolsDataSpan (*ElemToolsLoadFileHandlerPtr)(const char* path); ElemToolsAPI void ElemToolsConfigureFileIO(ElemToolsLoadFileHandlerPtr loadFileHandler); //------------------------------------------------------------------------ -// ##Module_Shaders## +// Module: Shaders //------------------------------------------------------------------------ // TODO: Change the list @@ -133,8 +146,8 @@ typedef enum ElemShaderLanguage_Unknown = 0, // High Level Shading Language used by DirectX. ElemShaderLanguage_Hlsl = 1, - // OpenGL Shading Language. - ElemShaderLanguage_Glsl = 2, + // Slang Shading Language. + ElemShaderLanguage_Slang = 2, // Metal Shading Language for Apple devices. ElemShaderLanguage_Msl = 3, // DirectX Intermediate Language. @@ -165,6 +178,7 @@ typedef struct bool DebugMode; // TODO: Add the ability to add an override for shader language + // TODO: Add the ability to choose Matrix packing options (default to row major for now?) } ElemCompileShaderOptions; /** @@ -204,37 +218,47 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph //------------------------------------------------------------------------ -// ##Module_Meshes## +// Module: SceneLoading //------------------------------------------------------------------------ typedef enum { - ElemMeshFormat_Obj = 0, -} ElemMeshFormat; + ElemSceneFormat_Obj = 0, +} ElemSceneFormat; typedef enum { - ElemMeshCoordinateSystem_LeftHanded = 0, - ElemMeshCoordinateSystem_RightHanded = 1 -} ElemMeshCoordinateSystem; + ElemSceneCoordinateSystem_LeftHanded = 0, + ElemSceneCoordinateSystem_RightHanded = 1 +} ElemSceneCoordinateSystem; typedef struct { - ElemToolsDataSpan Data; - uint32_t VertexSize; - // TODO: Add vertex description structure -} ElemVertexBuffer; + ElemSceneCoordinateSystem SceneCoordinateSystem; + // TODO: Add options to specify the vertex format wanted +} ElemLoadSceneOptions; typedef struct { - ElemMeshCoordinateSystem MeshCoordinateSystem; - // TODO: Add options to specify the vertex format wanted -} ElemLoadMeshOptions; + ElemSceneFormat MeshFormat; + ElemVertexBuffer VertexBuffer; + uint32_t VertexCount; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemLoadSceneResult; + +ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadSceneOptions* options); + + +//------------------------------------------------------------------------ +// Module: Meshes +//------------------------------------------------------------------------ // TODO: Allow to specify the offset of the vertex position? (For now we assume vertex position is at offset 0) typedef struct { // TODO: Allow bypass mesh format + // TODO: Support index buffers uint32_t Reserved; } ElemBuildMeshletsOptions; @@ -252,21 +276,6 @@ typedef struct uint32_t Length; } ElemMeshletSpan; -typedef struct -{ - uint32_t* Items; - uint32_t Length; -} ElemUInt32Span; - -typedef struct -{ - ElemMeshFormat MeshFormat; - ElemVertexBuffer VertexBuffer; - uint32_t VertexCount; - ElemToolsMessageSpan Messages; - bool HasErrors; -} ElemLoadMeshResult; - typedef struct { uint8_t MeshletMaxVertexCount; @@ -279,8 +288,6 @@ typedef struct bool HasErrors; } ElemBuildMeshletResult; -ElemToolsAPI ElemLoadMeshResult ElemLoadMesh(const char* path, const ElemLoadMeshOptions* options); - ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); // TODO: Do an optimize mesh function that can be used to optimize a normal mesh (for Raytracing for example) diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index ec20d662..7ae2db8a 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -26,7 +26,7 @@ typedef struct ElementalToolsFunctions void (*ElemToolsConfigureFileIO)(ElemToolsLoadFileHandlerPtr); bool (*ElemCanCompileShader)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform); ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *); - ElemLoadMeshResult (*ElemLoadMesh)(char const *, ElemLoadMeshOptions const *); + ElemLoadSceneResult (*ElemLoadScene)(char const *, ElemLoadSceneOptions const *); ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemBuildMeshletsOptions const *); } ElementalToolsFunctions; @@ -83,7 +83,7 @@ static bool LoadElementalToolsFunctionPointers(void) listElementalToolsFunctions.ElemToolsConfigureFileIO = (void (*)(ElemToolsLoadFileHandlerPtr))GetElementalToolsFunctionPointer("ElemToolsConfigureFileIO"); listElementalToolsFunctions.ElemCanCompileShader = (bool (*)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform))GetElementalToolsFunctionPointer("ElemCanCompileShader"); listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); - listElementalToolsFunctions.ElemLoadMesh = (ElemLoadMeshResult (*)(char const *, ElemLoadMeshOptions const *))GetElementalToolsFunctionPointer("ElemLoadMesh"); + listElementalToolsFunctions.ElemLoadScene = (ElemLoadSceneResult (*)(char const *, ElemLoadSceneOptions const *))GetElementalToolsFunctionPointer("ElemLoadScene"); listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); @@ -170,35 +170,35 @@ static inline ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGrap return listElementalToolsFunctions.ElemCompileShaderLibrary(graphicsApi, platform, path, options); } -static inline ElemLoadMeshResult ElemLoadMesh(char const * path, ElemLoadMeshOptions const * options) +static inline ElemLoadSceneResult ElemLoadScene(char const * path, ElemLoadSceneOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { assert(libraryElementalTools); #ifdef __cplusplus - ElemLoadMeshResult result = {}; + ElemLoadSceneResult result = {}; #else - ElemLoadMeshResult result = (ElemLoadMeshResult){0}; + ElemLoadSceneResult result = (ElemLoadSceneResult){0}; #endif return result; } - if (!listElementalToolsFunctions.ElemLoadMesh) + if (!listElementalToolsFunctions.ElemLoadScene) { - assert(listElementalToolsFunctions.ElemLoadMesh); + assert(listElementalToolsFunctions.ElemLoadScene); #ifdef __cplusplus - ElemLoadMeshResult result = {}; + ElemLoadSceneResult result = {}; #else - ElemLoadMeshResult result = (ElemLoadMeshResult){0}; + ElemLoadSceneResult result = (ElemLoadSceneResult){0}; #endif return result; } - return listElementalToolsFunctions.ElemLoadMesh(path, options); + return listElementalToolsFunctions.ElemLoadScene(path, options); } static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemBuildMeshletsOptions const * options) diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 9ab731b5..cc51ba43 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -11,7 +11,7 @@ #endif #define PRINT_COLOR_BUFFER(buffer, totalWidth) \ - { \ + { \ auto floatData = (float*)buffer.Items; \ \ for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) \ @@ -27,7 +27,7 @@ #define ASSERT_LOG_MESSAGE_DEBUG(message) { TestInitLog(); ASSERT_TRUE_MSG(strstr(testDebugLogs, message) != NULL, message); } #define ASSERT_COLOR_BUFFER(buffer, red, green, blue, alpha) \ - { \ + { \ auto floatData = (float*)buffer.Items; \ \ for (uint32_t i = 0; i < bufferData.Length / sizeof(float); i += 4) \ diff --git a/tests/ToolsTests/MeshLoaderTests.cpp b/tests/ToolsTests/MeshLoaderTests.cpp index 4c50b379..285c28ff 100644 --- a/tests/ToolsTests/MeshLoaderTests.cpp +++ b/tests/ToolsTests/MeshLoaderTests.cpp @@ -1,7 +1,7 @@ #include "ToolsTests.h" #include "utest.h" -auto cubeObjMeshSource = R"(o Cube +auto cubeObjSceneSource = R"(o Cube v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 1.000000 v -1.000000 -1.000000 1.000000 @@ -44,25 +44,25 @@ auto cubeObjMeshSource = R"(o Cube f 4/13/5 3/9/5 8/11/5 f 5/6/6 1/12/6 8/11/6)"; -struct MeshLoader_LoadMesh +struct SceneLoader_LoadScene { const char* Path; uint32_t ExpectedVertexCount; }; -UTEST_F_SETUP(MeshLoader_LoadMesh) +UTEST_F_SETUP(SceneLoader_LoadScene) { - AddTestFile("Cube.obj", { .Items = (uint8_t*)cubeObjMeshSource, .Length = (uint32_t)strlen(cubeObjMeshSource) }); + AddTestFile("Cube.obj", { .Items = (uint8_t*)cubeObjSceneSource, .Length = (uint32_t)strlen(cubeObjSceneSource) }); } -UTEST_F_TEARDOWN(MeshLoader_LoadMesh) +UTEST_F_TEARDOWN(SceneLoader_LoadScene) { // Act - auto result = ElemLoadMesh(utest_fixture->Path, NULL); + auto result = ElemLoadScene(utest_fixture->Path, NULL); // Assert ASSERT_FALSE(result.HasErrors); - ASSERT_EQ_MSG(result.VertexCount, utest_fixture->ExpectedVertexCount, "LoadMesh vertex count is not correct."); + ASSERT_EQ_MSG(result.VertexCount, utest_fixture->ExpectedVertexCount, "LoadScene vertex count is not correct."); ASSERT_GT_MSG(result.VertexBuffer.VertexSize, 0u, "Vertex size must be greater than 0."); ASSERT_GT_MSG(result.VertexBuffer.Data.Length, 0u, "VertexBuffer size must be greater than 0."); @@ -82,7 +82,7 @@ UTEST_F_TEARDOWN(MeshLoader_LoadMesh) // TODO: Add more data checks based on the format } -UTEST_F(MeshLoader_LoadMesh, Obj) +UTEST_F(SceneLoader_LoadScene, Obj) { utest_fixture->Path = "Cube.obj"; utest_fixture->ExpectedVertexCount = 36; From fe8d3449b28546d16db39d13454fdbede66b1a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 19 Dec 2024 14:35:53 +0100 Subject: [PATCH 39/93] Tools project structure change --- samples/Elemental/06-HelloMesh/main.c | 4 ++-- samples/Elemental/99-Renderer/main.c | 2 +- .../CMakeLists.txt | 2 +- .../{02-MeshCompiler => 02-SceneCompiler}/main.c | 0 src/ElementalTools/CMakeLists.txt | 8 ++++---- .../{Common => Meshes}/MeshletBuilder.cpp | 0 .../{ => Platforms}/Apple/CMakeLists.txt | 2 +- src/ElementalTools/{ => Platforms}/Apple/Info.plist | 0 .../Apple/Interop/DxilRootSignature.h | 0 .../{ => Platforms}/Apple/Interop/oaidl.h | 0 .../{ => Platforms}/Apple/Interop/ocidl.h | 0 .../{ => Platforms}/Apple/Interop/rpc.h | 0 .../{ => Platforms}/Apple/Interop/rpcndr.h | 0 .../{ => Platforms}/Apple/PreCompiledHeader.h | 0 .../{ => Platforms}/Linux/CMakeLists.txt | 2 +- .../Linux/Interop/DxilRootSignature.h | 0 .../{ => Platforms}/Linux/Interop/oaidl.h | 0 .../{ => Platforms}/Linux/Interop/ocidl.h | 0 .../{ => Platforms}/Linux/Interop/rpc.h | 0 .../{ => Platforms}/Linux/Interop/rpcndr.h | 0 .../{ => Platforms}/Linux/PreCompiledHeader.h | 0 .../{ => Platforms}/Microsoft/CMakeLists.txt | 2 +- .../{ => Platforms}/Microsoft/PreCompiledHeader.h | 0 .../MeshLoader.cpp => SceneLoading/SceneLoader.cpp} | 0 .../{Common => Shaders}/DirectXShaderCompiler.cpp | 0 .../{Common => Shaders}/DirectXShaderCompiler.h | 0 .../{Common => Shaders}/MetalShaderConverter.cpp | 0 .../{Common => Shaders}/MetalShaderConverter.h | 0 .../{Common => Shaders}/ShaderCompiler.cpp | 0 .../{Common => Shaders}/ShaderCompilerUtils.cpp | 0 .../{Common => Shaders}/ShaderCompilerUtils.h | 0 src/ElementalTools/{Common => }/ToolsUtils.cpp | 0 src/ElementalTools/{Common => }/ToolsUtils.h | 0 src/ElementalTools/{Common => }/UnityBuild.cpp | 12 ++++++------ .../{MeshLoaderTests.cpp => SceneLoaderTests.cpp} | 0 tests/ToolsTests/UnityBuild.cpp | 2 +- utilities/cmake/AppPackaging.cmake | 4 ++-- 37 files changed, 20 insertions(+), 20 deletions(-) rename samples/ElementalTools/{02-MeshCompiler => 02-SceneCompiler}/CMakeLists.txt (89%) rename samples/ElementalTools/{02-MeshCompiler => 02-SceneCompiler}/main.c (100%) rename src/ElementalTools/{Common => Meshes}/MeshletBuilder.cpp (100%) rename src/ElementalTools/{ => Platforms}/Apple/CMakeLists.txt (91%) rename src/ElementalTools/{ => Platforms}/Apple/Info.plist (100%) rename src/ElementalTools/{ => Platforms}/Apple/Interop/DxilRootSignature.h (100%) rename src/ElementalTools/{ => Platforms}/Apple/Interop/oaidl.h (100%) rename src/ElementalTools/{ => Platforms}/Apple/Interop/ocidl.h (100%) rename src/ElementalTools/{ => Platforms}/Apple/Interop/rpc.h (100%) rename src/ElementalTools/{ => Platforms}/Apple/Interop/rpcndr.h (100%) rename src/ElementalTools/{ => Platforms}/Apple/PreCompiledHeader.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/CMakeLists.txt (85%) rename src/ElementalTools/{ => Platforms}/Linux/Interop/DxilRootSignature.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/Interop/oaidl.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/Interop/ocidl.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/Interop/rpc.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/Interop/rpcndr.h (100%) rename src/ElementalTools/{ => Platforms}/Linux/PreCompiledHeader.h (100%) rename src/ElementalTools/{ => Platforms}/Microsoft/CMakeLists.txt (87%) rename src/ElementalTools/{ => Platforms}/Microsoft/PreCompiledHeader.h (100%) rename src/ElementalTools/{Common/MeshLoader.cpp => SceneLoading/SceneLoader.cpp} (100%) rename src/ElementalTools/{Common => Shaders}/DirectXShaderCompiler.cpp (100%) rename src/ElementalTools/{Common => Shaders}/DirectXShaderCompiler.h (100%) rename src/ElementalTools/{Common => Shaders}/MetalShaderConverter.cpp (100%) rename src/ElementalTools/{Common => Shaders}/MetalShaderConverter.h (100%) rename src/ElementalTools/{Common => Shaders}/ShaderCompiler.cpp (100%) rename src/ElementalTools/{Common => Shaders}/ShaderCompilerUtils.cpp (100%) rename src/ElementalTools/{Common => Shaders}/ShaderCompilerUtils.h (100%) rename src/ElementalTools/{Common => }/ToolsUtils.cpp (100%) rename src/ElementalTools/{Common => }/ToolsUtils.h (100%) rename src/ElementalTools/{Common => }/UnityBuild.cpp (52%) rename tests/ToolsTests/{MeshLoaderTests.cpp => SceneLoaderTests.cpp} (100%) diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index bb7470fd..ff9bf2f8 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -155,8 +155,8 @@ void InitSample(void* payload) applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - LoadMesh(&applicationPayload->TestMeshData, "kitten.mesh", applicationPayload); - //LoadMesh(&applicationPayload->TestMeshData, "buddha.mesh", applicationPayload); + LoadMesh(&applicationPayload->TestMeshData, "kitten.scene", applicationPayload); + //LoadMesh(&applicationPayload->TestMeshData, "buddha.scene", applicationPayload); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 61221330..43ed3f7b 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -175,7 +175,7 @@ void InitSample(void* payload) applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBufferReadDescriptor; CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - LoadMesh(&applicationPayload->TestMeshData, "sponza.mesh", applicationPayload); + LoadMesh(&applicationPayload->TestMeshData, "sponza.scene", applicationPayload); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); diff --git a/samples/ElementalTools/02-MeshCompiler/CMakeLists.txt b/samples/ElementalTools/02-SceneCompiler/CMakeLists.txt similarity index 89% rename from samples/ElementalTools/02-MeshCompiler/CMakeLists.txt rename to samples/ElementalTools/02-SceneCompiler/CMakeLists.txt index dbf60bcf..25ba4d14 100644 --- a/samples/ElementalTools/02-MeshCompiler/CMakeLists.txt +++ b/samples/ElementalTools/02-SceneCompiler/CMakeLists.txt @@ -1,5 +1,5 @@ if(NOT BUILD_FOR_IOS) - set(SAMPLE_NAME MeshCompiler) + set(SAMPLE_NAME SceneCompiler) add_executable(${SAMPLE_NAME} main.c) target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) diff --git a/samples/ElementalTools/02-MeshCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c similarity index 100% rename from samples/ElementalTools/02-MeshCompiler/main.c rename to samples/ElementalTools/02-SceneCompiler/main.c diff --git a/src/ElementalTools/CMakeLists.txt b/src/ElementalTools/CMakeLists.txt index 36482805..72744b9c 100644 --- a/src/ElementalTools/CMakeLists.txt +++ b/src/ElementalTools/CMakeLists.txt @@ -1,12 +1,12 @@ if(WIN32) - set(ELEM_PLATFORM_DIR Microsoft) + set(ELEM_PLATFORM_DIR Platforms/Microsoft) elseif(APPLE) - set(ELEM_PLATFORM_DIR Apple) + set(ELEM_PLATFORM_DIR Platforms/Apple) elseif(LINUX) - set(ELEM_PLATFORM_DIR Linux) + set(ELEM_PLATFORM_DIR Platforms/Linux) endif() -add_library(ElementalTools SHARED Common/UnityBuild.cpp) +add_library(ElementalTools SHARED UnityBuild.cpp) target_include_directories(ElementalTools PUBLIC .) target_precompile_headers(ElementalTools PRIVATE ${ELEM_PLATFORM_DIR}/PreCompiledHeader.h) diff --git a/src/ElementalTools/Common/MeshletBuilder.cpp b/src/ElementalTools/Meshes/MeshletBuilder.cpp similarity index 100% rename from src/ElementalTools/Common/MeshletBuilder.cpp rename to src/ElementalTools/Meshes/MeshletBuilder.cpp diff --git a/src/ElementalTools/Apple/CMakeLists.txt b/src/ElementalTools/Platforms/Apple/CMakeLists.txt similarity index 91% rename from src/ElementalTools/Apple/CMakeLists.txt rename to src/ElementalTools/Platforms/Apple/CMakeLists.txt index acdf1418..2fdc7cef 100644 --- a/src/ElementalTools/Apple/CMakeLists.txt +++ b/src/ElementalTools/Platforms/Apple/CMakeLists.txt @@ -1,4 +1,4 @@ -target_include_directories(ElementalTools PRIVATE ../../Elemental/Apple/) +target_include_directories(ElementalTools PRIVATE ../../../Elemental/Apple/) target_include_directories(ElementalTools PRIVATE ./Interop/) target_compile_options(ElementalTools PRIVATE -fms-extensions -fno-objc-arc -Wno-language-extension-token) target_link_options(ElementalTools PRIVATE "SHELL:-framework Metal" "SHELL:-framework Foundation") diff --git a/src/ElementalTools/Apple/Info.plist b/src/ElementalTools/Platforms/Apple/Info.plist similarity index 100% rename from src/ElementalTools/Apple/Info.plist rename to src/ElementalTools/Platforms/Apple/Info.plist diff --git a/src/ElementalTools/Apple/Interop/DxilRootSignature.h b/src/ElementalTools/Platforms/Apple/Interop/DxilRootSignature.h similarity index 100% rename from src/ElementalTools/Apple/Interop/DxilRootSignature.h rename to src/ElementalTools/Platforms/Apple/Interop/DxilRootSignature.h diff --git a/src/ElementalTools/Apple/Interop/oaidl.h b/src/ElementalTools/Platforms/Apple/Interop/oaidl.h similarity index 100% rename from src/ElementalTools/Apple/Interop/oaidl.h rename to src/ElementalTools/Platforms/Apple/Interop/oaidl.h diff --git a/src/ElementalTools/Apple/Interop/ocidl.h b/src/ElementalTools/Platforms/Apple/Interop/ocidl.h similarity index 100% rename from src/ElementalTools/Apple/Interop/ocidl.h rename to src/ElementalTools/Platforms/Apple/Interop/ocidl.h diff --git a/src/ElementalTools/Apple/Interop/rpc.h b/src/ElementalTools/Platforms/Apple/Interop/rpc.h similarity index 100% rename from src/ElementalTools/Apple/Interop/rpc.h rename to src/ElementalTools/Platforms/Apple/Interop/rpc.h diff --git a/src/ElementalTools/Apple/Interop/rpcndr.h b/src/ElementalTools/Platforms/Apple/Interop/rpcndr.h similarity index 100% rename from src/ElementalTools/Apple/Interop/rpcndr.h rename to src/ElementalTools/Platforms/Apple/Interop/rpcndr.h diff --git a/src/ElementalTools/Apple/PreCompiledHeader.h b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h similarity index 100% rename from src/ElementalTools/Apple/PreCompiledHeader.h rename to src/ElementalTools/Platforms/Apple/PreCompiledHeader.h diff --git a/src/ElementalTools/Linux/CMakeLists.txt b/src/ElementalTools/Platforms/Linux/CMakeLists.txt similarity index 85% rename from src/ElementalTools/Linux/CMakeLists.txt rename to src/ElementalTools/Platforms/Linux/CMakeLists.txt index 7df60e2f..bba19a77 100644 --- a/src/ElementalTools/Linux/CMakeLists.txt +++ b/src/ElementalTools/Platforms/Linux/CMakeLists.txt @@ -1,4 +1,4 @@ -target_include_directories(ElementalTools PRIVATE ../../Elemental/Linux/) +target_include_directories(ElementalTools PRIVATE ../../../Elemental/Linux/) target_include_directories(ElementalTools PRIVATE ./Interop/) target_compile_options(ElementalTools PRIVATE -fms-extensions -Wno-language-extension-token) diff --git a/src/ElementalTools/Linux/Interop/DxilRootSignature.h b/src/ElementalTools/Platforms/Linux/Interop/DxilRootSignature.h similarity index 100% rename from src/ElementalTools/Linux/Interop/DxilRootSignature.h rename to src/ElementalTools/Platforms/Linux/Interop/DxilRootSignature.h diff --git a/src/ElementalTools/Linux/Interop/oaidl.h b/src/ElementalTools/Platforms/Linux/Interop/oaidl.h similarity index 100% rename from src/ElementalTools/Linux/Interop/oaidl.h rename to src/ElementalTools/Platforms/Linux/Interop/oaidl.h diff --git a/src/ElementalTools/Linux/Interop/ocidl.h b/src/ElementalTools/Platforms/Linux/Interop/ocidl.h similarity index 100% rename from src/ElementalTools/Linux/Interop/ocidl.h rename to src/ElementalTools/Platforms/Linux/Interop/ocidl.h diff --git a/src/ElementalTools/Linux/Interop/rpc.h b/src/ElementalTools/Platforms/Linux/Interop/rpc.h similarity index 100% rename from src/ElementalTools/Linux/Interop/rpc.h rename to src/ElementalTools/Platforms/Linux/Interop/rpc.h diff --git a/src/ElementalTools/Linux/Interop/rpcndr.h b/src/ElementalTools/Platforms/Linux/Interop/rpcndr.h similarity index 100% rename from src/ElementalTools/Linux/Interop/rpcndr.h rename to src/ElementalTools/Platforms/Linux/Interop/rpcndr.h diff --git a/src/ElementalTools/Linux/PreCompiledHeader.h b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h similarity index 100% rename from src/ElementalTools/Linux/PreCompiledHeader.h rename to src/ElementalTools/Platforms/Linux/PreCompiledHeader.h diff --git a/src/ElementalTools/Microsoft/CMakeLists.txt b/src/ElementalTools/Platforms/Microsoft/CMakeLists.txt similarity index 87% rename from src/ElementalTools/Microsoft/CMakeLists.txt rename to src/ElementalTools/Platforms/Microsoft/CMakeLists.txt index b9e442d7..f07383a5 100644 --- a/src/ElementalTools/Microsoft/CMakeLists.txt +++ b/src/ElementalTools/Platforms/Microsoft/CMakeLists.txt @@ -1,6 +1,6 @@ target_compile_options(ElementalTools PRIVATE -EHs) -target_include_directories(ElementalTools PRIVATE ../../Elemental/Microsoft) +target_include_directories(ElementalTools PRIVATE ../../../Elemental/Microsoft) target_link_libraries(ElementalTools PRIVATE Dwmapi UxTheme onecore) target_compile_definitions(ElementalTools PRIVATE UNICODE _UNICODE _WINDOWS _WIN32) diff --git a/src/ElementalTools/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h similarity index 100% rename from src/ElementalTools/Microsoft/PreCompiledHeader.h rename to src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h diff --git a/src/ElementalTools/Common/MeshLoader.cpp b/src/ElementalTools/SceneLoading/SceneLoader.cpp similarity index 100% rename from src/ElementalTools/Common/MeshLoader.cpp rename to src/ElementalTools/SceneLoading/SceneLoader.cpp diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.cpp b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp similarity index 100% rename from src/ElementalTools/Common/DirectXShaderCompiler.cpp rename to src/ElementalTools/Shaders/DirectXShaderCompiler.cpp diff --git a/src/ElementalTools/Common/DirectXShaderCompiler.h b/src/ElementalTools/Shaders/DirectXShaderCompiler.h similarity index 100% rename from src/ElementalTools/Common/DirectXShaderCompiler.h rename to src/ElementalTools/Shaders/DirectXShaderCompiler.h diff --git a/src/ElementalTools/Common/MetalShaderConverter.cpp b/src/ElementalTools/Shaders/MetalShaderConverter.cpp similarity index 100% rename from src/ElementalTools/Common/MetalShaderConverter.cpp rename to src/ElementalTools/Shaders/MetalShaderConverter.cpp diff --git a/src/ElementalTools/Common/MetalShaderConverter.h b/src/ElementalTools/Shaders/MetalShaderConverter.h similarity index 100% rename from src/ElementalTools/Common/MetalShaderConverter.h rename to src/ElementalTools/Shaders/MetalShaderConverter.h diff --git a/src/ElementalTools/Common/ShaderCompiler.cpp b/src/ElementalTools/Shaders/ShaderCompiler.cpp similarity index 100% rename from src/ElementalTools/Common/ShaderCompiler.cpp rename to src/ElementalTools/Shaders/ShaderCompiler.cpp diff --git a/src/ElementalTools/Common/ShaderCompilerUtils.cpp b/src/ElementalTools/Shaders/ShaderCompilerUtils.cpp similarity index 100% rename from src/ElementalTools/Common/ShaderCompilerUtils.cpp rename to src/ElementalTools/Shaders/ShaderCompilerUtils.cpp diff --git a/src/ElementalTools/Common/ShaderCompilerUtils.h b/src/ElementalTools/Shaders/ShaderCompilerUtils.h similarity index 100% rename from src/ElementalTools/Common/ShaderCompilerUtils.h rename to src/ElementalTools/Shaders/ShaderCompilerUtils.h diff --git a/src/ElementalTools/Common/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp similarity index 100% rename from src/ElementalTools/Common/ToolsUtils.cpp rename to src/ElementalTools/ToolsUtils.cpp diff --git a/src/ElementalTools/Common/ToolsUtils.h b/src/ElementalTools/ToolsUtils.h similarity index 100% rename from src/ElementalTools/Common/ToolsUtils.h rename to src/ElementalTools/ToolsUtils.h diff --git a/src/ElementalTools/Common/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp similarity index 52% rename from src/ElementalTools/Common/UnityBuild.cpp rename to src/ElementalTools/UnityBuild.cpp index 6efd8f1d..47848a28 100644 --- a/src/ElementalTools/Common/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -1,16 +1,16 @@ #include "ToolsUtils.cpp" -#include "ShaderCompiler.cpp" -#include "ShaderCompilerUtils.cpp" -#include "DirectXShaderCompiler.cpp" +#include "Shaders/ShaderCompiler.cpp" +#include "Shaders/ShaderCompilerUtils.cpp" +#include "Shaders/DirectXShaderCompiler.cpp" #ifndef __linux__ -#include "MetalShaderConverter.cpp" +#include "Shaders/MetalShaderConverter.cpp" #endif #include "fast_obj.c" +#include "SceneLoading/SceneLoader.cpp" -#include "MeshLoader.cpp" -#include "MeshletBuilder.cpp" +#include "Meshes/MeshletBuilder.cpp" #include "SystemFunctions.cpp" #include "SystemDictionary.cpp" diff --git a/tests/ToolsTests/MeshLoaderTests.cpp b/tests/ToolsTests/SceneLoaderTests.cpp similarity index 100% rename from tests/ToolsTests/MeshLoaderTests.cpp rename to tests/ToolsTests/SceneLoaderTests.cpp diff --git a/tests/ToolsTests/UnityBuild.cpp b/tests/ToolsTests/UnityBuild.cpp index d2522f26..465d3353 100644 --- a/tests/ToolsTests/UnityBuild.cpp +++ b/tests/ToolsTests/UnityBuild.cpp @@ -1,6 +1,6 @@ #include "ToolsTests.cpp" #include "ShaderCompilerTests.cpp" -#include "MeshLoaderTests.cpp" +#include "SceneLoaderTests.cpp" #include "MeshBuilderTests.cpp" UTEST_STATE(); diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index cc698b94..b7abb85f 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -123,8 +123,8 @@ function(configure_resource_compilation target_name resource_list) # Format: Name|BinaryName|SourceExtensions|DestExtension|DefaultParams... set(COMPILERS_LIST "HLSL|ShaderCompiler|.hlsl|.shader|${SHADER_COMPILER_DEFAULT_OPTIONS}" - "MESH|MeshCompiler|.obj|.mesh|${MESH_COMPILER_DEFAULT_OPTIONS}" - "MESH|MeshCompiler|.gltf|.mesh|${MESH_COMPILER_DEFAULT_OPTIONS}" + "MESH|SceneCompiler|.obj|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" + "MESH|SceneCompiler|.gltf|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" ) set(all_compiled_resources "") From 449c0fe685a20c404a1807809b165c683f9c8508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 24 Dec 2024 10:58:49 +0100 Subject: [PATCH 40/93] Rework scene compilation and loader --- CMakeLists.txt | 4 +- REFERENCES.MD | 3 + samples/CMakeLists.txt | 1 + samples/Common/SampleGpuMemory.h | 68 +++++ samples/Common/SampleInputsCamera.h | 2 + samples/Common/SampleMesh.h | 19 -- samples/Common/SampleScene.h | 30 +++ samples/Common/SampleSceneLoader.h | 89 ++++++ samples/Common/SampleUtils.h | 172 +++++++----- samples/Elemental/02-HelloTriangle/main.c | 2 +- samples/Elemental/03-HelloInputs/main.c | 2 +- samples/Elemental/04-HelloCompute/main.c | 4 +- samples/Elemental/05-HelloImGui/main.c | 2 +- samples/Elemental/06-HelloMesh/main.c | 72 +---- .../99-Renderer/Data/RenderMesh.hlsl | 26 +- samples/Elemental/99-Renderer/main.c | 163 +++++------ .../ElementalTools/01-ShaderCompiler/main.c | 9 +- .../ElementalTools/02-SceneCompiler/main.c | 179 +++++++----- .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 1 + .../Common/Graphics/Vulkan/VulkanResource.cpp | 1 + .../Graphics/DirectX12GraphicsDevice.cpp | 2 - src/ElementalTools/ElementalTools.h | 59 +++- src/ElementalTools/Meshes/MeshletBuilder.cpp | 3 +- .../SceneLoading/SceneLoader.cpp | 207 +++----------- src/ElementalTools/SceneLoading/SceneLoader.h | 5 + .../SceneLoading/SceneLoaderObj.cpp | 254 ++++++++++++++++++ .../Shaders/DirectXShaderCompiler.cpp | 7 +- src/ElementalTools/Shaders/ShaderCompiler.cpp | 21 +- src/ElementalTools/ToolsUtils.cpp | 13 + src/ElementalTools/ToolsUtils.h | 4 + tests/ToolsTests/MeshBuilderTests.cpp | 6 +- tests/ToolsTests/SceneLoaderTests.cpp | 28 +- tests/ToolsTests/ToolsTests.h | 1 - 33 files changed, 906 insertions(+), 553 deletions(-) create mode 100644 samples/Common/SampleGpuMemory.h delete mode 100644 samples/Common/SampleMesh.h create mode 100644 samples/Common/SampleScene.h create mode 100644 samples/Common/SampleSceneLoader.h create mode 100644 src/ElementalTools/SceneLoading/SceneLoader.h create mode 100644 src/ElementalTools/SceneLoading/SceneLoaderObj.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fa096f8..c33c7223 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,12 +49,12 @@ add_compile_options($<$:-Wno-unused-parameter>) add_compile_options($<$:-Wno-missing-field-initializers>) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fansi-escape-codes -fcolor-diagnostics -D_CRT_SECURE_NO_WARNINGS") set(CMAKE_CXX_STANDARD "23") set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fansi-escape-codes -fcolor-diagnostics -D_CRT_SECURE_NO_WARNINGS") set(CMAKE_C_STANDARD "23") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/REFERENCES.MD b/REFERENCES.MD index 45996388..3f73918c 100644 --- a/REFERENCES.MD +++ b/REFERENCES.MD @@ -3,3 +3,6 @@ https://github.com/zeux/niagara https://therealmjp.github.io/posts/breaking-down-barriers-part-1-whats-a-barrier/ https://therealmjp.github.io/posts/gpu-memory-pool/ + +# HDR +https://github.com/h3r2tic/tony-mc-mapface/blob/main/shader/tony_mc_mapface.hlsl diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 4b860f38..7e8ae236 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -1,2 +1,3 @@ add_library(SampleCommon INTERFACE) target_include_directories(SampleCommon INTERFACE ./Common/) +target_link_libraries(SampleCommon INTERFACE ElementalInterface) diff --git a/samples/Common/SampleGpuMemory.h b/samples/Common/SampleGpuMemory.h new file mode 100644 index 00000000..d821d4cc --- /dev/null +++ b/samples/Common/SampleGpuMemory.h @@ -0,0 +1,68 @@ +#pragma once + +#include "Elemental.h" +#include "SampleUtils.h" + +typedef struct +{ + ElemGraphicsDevice GraphicsDevice; + ElemGraphicsHeap GraphicsHeap; + uint32_t CurrentHeapOffset; +} SampleGpuMemory; + +typedef struct +{ + ElemGraphicsResource Buffer; + ElemGraphicsResourceDescriptor ReadDescriptor; +} SampleGpuBuffer; + +SampleGpuMemory SampleCreateGpuMemory(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes) +{ + // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues + // TODO: Having GPU Upload is still annoying 😞 + ElemGraphicsHeap graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, sizeInBytes, &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + + return (SampleGpuMemory) + { + .GraphicsDevice = graphicsDevice, + .GraphicsHeap = graphicsHeap, + .CurrentHeapOffset = 0u + }; +} + +void SampleFreeGpuMemory(SampleGpuMemory* gpuMemory) +{ + ElemFreeGraphicsHeap(gpuMemory->GraphicsHeap); + gpuMemory->GraphicsHeap = ELEM_HANDLE_NULL; +} + +// TODO: To remove when IOQueues +SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, const void* dataPointer, uint32_t sizeInBytes, const char* debugName) +{ + // TODO: Alignment should be used with the offset before adding the size of the resource! + ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(gpuMemory->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, &(ElemGraphicsResourceInfoOptions) { .DebugName = debugName }); + + gpuMemory->CurrentHeapOffset = SampleAlignValue(gpuMemory->CurrentHeapOffset, bufferDescription.Alignment); + ElemGraphicsResource buffer = ElemCreateGraphicsResource(gpuMemory->GraphicsHeap, gpuMemory->CurrentHeapOffset, &bufferDescription); + gpuMemory->CurrentHeapOffset += bufferDescription.SizeInBytes; + + ElemGraphicsResourceDescriptor readDescriptor = ElemCreateGraphicsResourceDescriptor(buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); + + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(buffer); + memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); + + return (SampleGpuBuffer) + { + .Buffer = buffer, + .ReadDescriptor = readDescriptor + }; +} + +void SampleFreeGpuBuffer(SampleGpuBuffer* gpuBuffer) +{ + ElemFreeGraphicsResourceDescriptor(gpuBuffer->ReadDescriptor, NULL); + gpuBuffer->ReadDescriptor = ELEM_HANDLE_NULL; + + ElemFreeGraphicsResource(gpuBuffer->Buffer, NULL); + gpuBuffer->Buffer = ELEM_HANDLE_NULL; +} diff --git a/samples/Common/SampleInputsCamera.h b/samples/Common/SampleInputsCamera.h index 5b719666..ff121870 100644 --- a/samples/Common/SampleInputsCamera.h +++ b/samples/Common/SampleInputsCamera.h @@ -89,6 +89,8 @@ void SampleInputsCameraInit(SampleInputsCamera* inputs) // TODO: Temporary Init code for now inputs->State.Camera.Position = (SampleVector3) { 0.0f, 0.0f, -0.0f }; inputs->State.Camera.Rotation = V3Zero; + + // TODO: Allow the reset of camera to the passed initial position when we press a button } SampleVector3 ComputeDeltaAndVelocity(SampleVector3* currentVelocity, SampleVector3 directionVector, float accelerationFactor, float frictionFactor, float deltaTimeInSeconds) diff --git a/samples/Common/SampleMesh.h b/samples/Common/SampleMesh.h deleted file mode 100644 index bf3a5fee..00000000 --- a/samples/Common/SampleMesh.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -typedef struct -{ - char FileId[4]; - uint32_t MeshletCount; - uint8_t MeshletMaxVertexCount; - uint8_t MeshletMaxTriangleCount; - uint32_t VertexBufferOffset; - uint32_t VertexBufferSizeInBytes; - uint32_t MeshletBufferOffset; - uint32_t MeshletBufferSizeInBytes; - uint32_t MeshletVertexIndexBufferOffset; - uint32_t MeshletVertexIndexBufferSizeInBytes; - uint32_t MeshletTriangleIndexBufferOffset; - uint32_t MeshletTriangleIndexBufferSizeInBytes; -} SampleMeshHeader; diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h new file mode 100644 index 00000000..6fa5fc82 --- /dev/null +++ b/samples/Common/SampleScene.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +typedef struct +{ + char FileId[5]; + uint32_t MeshCount; +} SampleSceneHeader; + +typedef struct +{ + // TODO: Vertex size, etc. + uint32_t MeshPartCount; +} SampleMeshHeader; + +typedef struct +{ + uint32_t MeshletCount; + uint32_t VertexBufferOffset; + uint32_t MeshletOffset; + uint32_t MeshletVertexIndexOffset; + uint32_t MeshletTriangleIndexOffset; +} SampleMeshPartHeader; + +typedef struct +{ + uint32_t Offset; + uint32_t SizeInBytes; +} SampleDataBlockEntry; diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h new file mode 100644 index 00000000..05b91a8c --- /dev/null +++ b/samples/Common/SampleSceneLoader.h @@ -0,0 +1,89 @@ +#pragma once + +#include "Elemental.h" +#include "SampleUtils.h" +#include "SampleScene.h" +#include "SampleGpuMemory.h" + +typedef struct +{ + uint32_t MeshPartCount; + SampleMeshPartHeader* MeshParts; + SampleGpuBuffer MeshBuffer; +} SampleMeshData; + +typedef struct +{ + uint32_t MeshCount; + SampleMeshData* Meshes; +} SampleSceneData; + +void SampleLoadMesh(FILE* file, const SampleDataBlockEntry* datablock, SampleGpuMemory* gpuMemory, SampleMeshData* meshData, const char* debugName) +{ + fseek(file, datablock->Offset, SEEK_SET); + + SampleMeshHeader meshHeader; + fread(&meshHeader, sizeof(SampleMeshHeader), 1, file); + + meshData->MeshPartCount = meshHeader.MeshPartCount; + meshData->MeshParts = (SampleMeshPartHeader*)malloc(sizeof(SampleMeshPartHeader) * meshHeader.MeshPartCount); + fread(meshData->MeshParts, sizeof(SampleMeshPartHeader), meshHeader.MeshPartCount, file); + + uint32_t currentOffset = ftell(file) - datablock->Offset; + uint32_t meshBufferSizeInBytes = datablock->SizeInBytes - currentOffset; + assert(meshBufferSizeInBytes > 0); + + uint8_t* meshBufferData = (uint8_t*)malloc(sizeof(uint8_t) * meshBufferSizeInBytes); + fread(meshBufferData, sizeof(uint8_t), meshBufferSizeInBytes, file); + + // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! + meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshBufferSizeInBytes, debugName); +} + +void SampleFreeMesh(SampleMeshData* meshData) +{ + SampleFreeGpuBuffer(&meshData->MeshBuffer); + + // TODO: Free mallocs +} + +void SampleLoadScene(const char* path, SampleGpuMemory* gpuMemory, SampleSceneData* sceneData) +{ + // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! + + FILE* file = SampleOpenFile(path, true); + assert(file); + + SampleSceneHeader sceneHeader; + fread(&sceneHeader, sizeof(sceneHeader), 1, file); + + if (!(sceneHeader.FileId[0] == 'S' && sceneHeader.FileId[1] == 'C' && sceneHeader.FileId[2] == 'E' && sceneHeader.FileId[3] == 'N' && sceneHeader.FileId[4] == 'E')) + { + printf("ERROR: Wrong scene format\n"); + } + + printf("OK Meshes Count: %d\n", sceneHeader.MeshCount); + + sceneData->MeshCount = sceneHeader.MeshCount; + + // TODO: Replace malloc with an utility function that we will replace + sceneData->Meshes = (SampleMeshData*)malloc(sizeof(SampleMeshData) * sceneHeader.MeshCount); + + SampleDataBlockEntry* meshDataBlocks = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * sceneHeader.MeshCount); + fread(meshDataBlocks, sizeof(SampleDataBlockEntry), sceneHeader.MeshCount, file); + + for (uint32_t i = 0; i < sceneData->MeshCount; i++) + { + SampleLoadMesh(file, &meshDataBlocks[i], gpuMemory, &sceneData->Meshes[i], "Mesh"); + } + + fclose(file); +} + +void SampleFreeScene(const SampleSceneData* sceneData) +{ + for (uint32_t i = 0; i < sceneData->MeshCount; i++) + { + SampleFreeMesh(&sceneData->Meshes[i]); + } +} diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index d8a56a49..a4bd9ca2 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -8,6 +8,8 @@ #ifdef ElemToolsAPI typedef ElemToolsDataSpan ElemDataSpan; +#else +#include "../Elemental/Elemental.h" #endif #ifndef _WIN32 @@ -40,77 +42,67 @@ uint32_t SampleAlignValue(uint32_t value, uint32_t alignment) // ----------------------------------------------------------------------------- // I/O Functions // ----------------------------------------------------------------------------- -void CopyString(char* destination, uint32_t destinationLength, const char* source, uint32_t sourceLength) -{ - #ifdef _WIN32 - strncpy_s(destination, destinationLength, source, sourceLength); - #else - strncpy(destination, source, sourceLength); - #endif -} - -void SampleGetFullPath(char* destination, const char* path) +void SampleGetFullPath(char* destination, const char* path, bool prefixData) { memset(destination, 0, MAX_PATH); char* pointer = destination; - #ifdef ElemAPI + #ifndef ElemToolsAPI ElemSystemInfo systemInfo = ElemGetSystemInfo(); - CopyString(destination, MAX_PATH, systemInfo.ApplicationPath, strlen(systemInfo.ApplicationPath)); + strncpy(destination, systemInfo.ApplicationPath, strlen(systemInfo.ApplicationPath)); pointer = destination + strlen(systemInfo.ApplicationPath); - const char* folderPrefix = "Data/"; + const char* folderPrefix = "./"; - if (systemInfo.Platform == ElemPlatform_MacOS) + if (prefixData) { - folderPrefix = "../Resources/"; - } - else if (systemInfo.Platform == ElemPlatform_iOS) + if (systemInfo.Platform == ElemPlatform_MacOS) + { + folderPrefix = "../Resources/"; + } + else if (systemInfo.Platform == ElemPlatform_iOS) + { + folderPrefix = "./"; + } + else { - folderPrefix = "./"; + folderPrefix = "Data/"; + } } - CopyString(pointer, pointer - destination, folderPrefix, strlen(folderPrefix)); + strncpy(pointer, folderPrefix, strlen(folderPrefix)); pointer = pointer + strlen(folderPrefix); #endif - CopyString(pointer, MAX_PATH - (int32_t)(pointer - destination), path, strlen(path)); + strncpy(pointer, path, strlen(path)); } -ElemDataSpan SampleReadFile(const char* filename) +FILE* SampleOpenFile(const char* filename, bool prefixData) { char absolutePath[MAX_PATH]; - SampleGetFullPath(absolutePath, filename); + SampleGetFullPath(absolutePath, filename, prefixData); - printf("Read path: %s\n", absolutePath); + //printf("Read path: %s\n", absolutePath); - #ifdef _WIN32 - FILE* file; - fopen_s(&file, absolutePath, "rb"); - #else - FILE* file = fopen(absolutePath, "rb"); - #endif + return fopen(absolutePath, "rb"); +} +// TODO: To remove? +ElemDataSpan SampleReadFile(const char* filename, bool prefixData) +{ + FILE* file = SampleOpenFile(filename, prefixData); + if (file == NULL) { - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; + return (ElemDataSpan) {}; } if (fseek(file, 0, SEEK_END) != 0) { fclose(file); - - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; + return (ElemDataSpan) {}; } long fileSize = ftell(file); @@ -118,12 +110,7 @@ ElemDataSpan SampleReadFile(const char* filename) if (fileSize == -1) { fclose(file); - - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; + return (ElemDataSpan) {}; } rewind(file); @@ -134,12 +121,7 @@ ElemDataSpan SampleReadFile(const char* filename) if (buffer == NULL) { fclose(file); - - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; + return (ElemDataSpan) {}; } size_t bytesRead = fread(buffer, 1, fileSize, file); @@ -148,12 +130,7 @@ ElemDataSpan SampleReadFile(const char* filename) { free(buffer); fclose(file); - - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; + return (ElemDataSpan) {}; } fclose(file); @@ -165,22 +142,12 @@ ElemDataSpan SampleReadFile(const char* filename) }; } +// TODO: Remove those? int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) { - if (filename == NULL || data.Length == 0) - { - printf("ERROR 1\n"); - return -1; - } - const char* fileMode = append ? "ab" : "wb"; - #ifdef _WIN32 - FILE* file; - fopen_s(&file, filename, fileMode); - #else FILE* file = fopen(filename, fileMode); - #endif if (file == NULL) { @@ -200,6 +167,37 @@ int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) return 0; // Success } +uint32_t SampleGetFileSize(const char* filename) +{ + if (filename == NULL) + { + printf("ERROR 1\n"); + return -1; + } + + FILE* file = fopen(filename, "ab"); + + if (file == NULL) + { + printf("ERROR 2\n"); + return -1; + } + + fseek(file, 0, SEEK_END); + uint32_t fileSize = ftell(file); + fclose(file); + + return fileSize; +} + +int SampleWriteDataToApplicationFile(const char* filename, ElemDataSpan data, bool append) +{ + char absolutePath[MAX_PATH]; + SampleGetFullPath(absolutePath, filename, false); + + return SampleWriteDataToFile(absolutePath, data, append); +} + ElemDataSpan SampleReadLine(ElemDataSpan* data) { for (uint32_t i = 0; i < data->Length; i++) @@ -456,6 +454,7 @@ typedef struct { bool PreferVulkan; bool PreferFullScreen; + bool DisableDiagnostics; } SampleAppSettings; SampleAppSettings SampleParseAppSettings(int argc, const char* argv[]) @@ -473,7 +472,42 @@ SampleAppSettings SampleParseAppSettings(int argc, const char* argv[]) { appSettings.PreferFullScreen = true; } + + if (strcmp(argv[i], "--disable-diagnostics") == 0) + { + appSettings.DisableDiagnostics = true; + } } return appSettings; } + +#ifdef ElemToolsAPI +void DisplayOutputMessages(const char* prefix, ElemToolsMessageSpan messages) +{ + for (uint32_t i = 0; i < messages.Length; i++) + { + ElemToolsMessage* message = &messages.Items[i]; + + printf("["); + printf("\033[36m%s\033[0m]", prefix); + + switch (message->Type) + { + case ElemToolsMessageType_Error: + printf("\033[31m Error:"); + break; + + case ElemToolsMessageType_Warning: + printf("\033[33m Warning:"); + break; + + default: + printf("\033[0m"); + } + + printf(" %s\033[0m\n", message->Message); + fflush(stdout); + } +} +#endif diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c index bad3d470..de77d802 100644 --- a/samples/Elemental/02-HelloTriangle/main.c +++ b/samples/Elemental/02-HelloTriangle/main.c @@ -33,7 +33,7 @@ void InitSample(void* payload) applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .UpdatePayload = payload }); ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Triangle.shader": "Triangle_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Triangle.shader": "Triangle_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 5ec150f8..404a55ea 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -176,7 +176,7 @@ void InitSample(void* payload) applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .UpdatePayload = payload }); ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(applicationPayload->SwapChain); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Triangle.shader": "Triangle_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Triangle.shader": "Triangle_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/04-HelloCompute/main.c index b4278644..dd48df92 100644 --- a/samples/Elemental/04-HelloCompute/main.c +++ b/samples/Elemental/04-HelloCompute/main.c @@ -100,7 +100,7 @@ void InitSample(void* payload) CreateRenderTexture(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Fractal.shader": "Fractal_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Fractal.shader": "Fractal_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->ComputePipeline = ElemCompileComputePipelineState(applicationPayload->GraphicsDevice, &(ElemComputePipelineStateParameters) { @@ -111,7 +111,7 @@ void InitSample(void* payload) ElemFreeShaderLibrary(shaderLibrary); - shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Tonemap.shader": "Tonemap_vulkan.shader"); + shaderData = SampleReadFile(!applicationPayload->PreferVulkan ? "Tonemap.shader": "Tonemap_vulkan.shader", true); shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 792ec363..3a808ebb 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -78,7 +78,7 @@ void ImGuiInitBackend(ApplicationPayload* payload) ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(payload->SwapChain); - ElemDataSpan shaderData = SampleReadFile(!payload->PreferVulkan ? "RenderImGui.shader": "RenderImGui_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!payload->PreferVulkan ? "RenderImGui.shader": "RenderImGui_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(payload->GraphicsDevice, shaderData); imGuiData->RenderPipeline = ElemCompileGraphicsPipelineState(payload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index ff9bf2f8..b99712a3 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -3,8 +3,12 @@ #include "SampleMath.h" #include "SampleInputsApplication.h" #include "SampleInputsModelViewer.h" -#include "SampleMesh.h" +#include "SampleSceneLoader.h" +// TODO: Take all the code from the common headers and integrate it here + + +/* typedef struct { uint32_t VertexBuffer; @@ -18,18 +22,6 @@ typedef struct uint32_t MeshletCount; } ShaderParameters; -typedef struct -{ - uint32_t MeshletCount; - ElemGraphicsResource VertexBuffer; - ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; - ElemGraphicsResource MeshletBuffer; - ElemGraphicsResourceDescriptor MeshletBufferReadDescriptor; - ElemGraphicsResource MeshletVertexIndexBuffer; - ElemGraphicsResourceDescriptor MeshletVertexIndexBufferReadDescriptor; - ElemGraphicsResource MeshletTriangleIndexBuffer; - ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; -} MeshData; // TODO: Group common variables into separate structs typedef struct @@ -48,7 +40,7 @@ typedef struct ShaderParameters ShaderParameters; SampleInputsApplication InputsApplication; SampleInputsModelViewer InputsModelViewer; - MeshData TestMeshData; + SampleSceneData TestSceneData; } ApplicationPayload; void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); @@ -86,46 +78,6 @@ void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceD memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); } -void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) -{ - // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! - // Add a parameter to specify the length we want to read and the offset - ElemDataSpan meshFileData = SampleReadFile(path); - - uint8_t* fileDataPointer = meshFileData.Items; - SampleMeshHeader* meshHeader = (SampleMeshHeader*)fileDataPointer; - - if (meshHeader->FileId[0] == 'M' && meshHeader->FileId[1] == 'E' && meshHeader->FileId[2] == 'S' && meshHeader->FileId[3] == 'H') - { - printf("OK Meshlet Count: %d\n", meshHeader->MeshletCount); - } - - meshData->MeshletCount = meshHeader->MeshletCount; - - CreateAndUploadDataTemp(&meshData->VertexBuffer, &meshData->VertexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->VertexBufferOffset, meshHeader->VertexBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletBuffer, &meshData->MeshletBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletBufferOffset, meshHeader->MeshletBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletVertexIndexBuffer, &meshData->MeshletVertexIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletVertexIndexBufferOffset, meshHeader->MeshletVertexIndexBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletTriangleIndexBuffer, &meshData->MeshletTriangleIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletTriangleIndexBufferOffset, meshHeader->MeshletTriangleIndexBufferSizeInBytes); - - applicationPayload->ShaderParameters.VertexBuffer = meshData->VertexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletBuffer = meshData->MeshletBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = meshData->MeshletVertexIndexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = meshData->MeshletTriangleIndexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletCount = meshData->MeshletCount; -} - -void FreeMesh(MeshData* meshData) -{ - ElemFreeGraphicsResourceDescriptor(meshData->VertexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->VertexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletVertexIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletVertexIndexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletTriangleIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletTriangleIndexBuffer, NULL); -} - void InitSample(void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; @@ -155,10 +107,10 @@ void InitSample(void* payload) applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - LoadMesh(&applicationPayload->TestMeshData, "kitten.scene", applicationPayload); + //LoadMesh(&applicationPayload->TestMeshData, "kitten.scene", applicationPayload); //LoadMesh(&applicationPayload->TestMeshData, "buddha.scene", applicationPayload); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { @@ -196,7 +148,7 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); - FreeMesh(&applicationPayload->TestMeshData); + //FreeMesh(&applicationPayload->TestMeshData); ElemFreePipelineState(applicationPayload->GraphicsPipeline); ElemFreeSwapChain(applicationPayload->SwapChain); @@ -275,7 +227,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + //ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); ElemEndRenderPass(commandList); @@ -309,4 +261,8 @@ int main(int argc, const char* argv[]) .FreeHandler = FreeSample, .Payload = &payload }); +}*/ + +int main(int argc, const char* argv[]) +{ } diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index e70f692e..62cb45a3 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -1,12 +1,13 @@ struct ShaderParameters { uint32_t FrameDataBufferIndex; - uint32_t VertexBufferIndex; - uint32_t MeshletBufferIndex; - uint32_t MeshletVertexIndexBufferIndex; - uint32_t MeshletTriangleIndexBufferIndex; + uint32_t MeshBuffer; + uint32_t MeshletCount; // TODO: Not used + uint32_t VertexBufferOffset; + uint32_t MeshletOffset; + uint32_t MeshletVertexIndexOffset; + uint32_t MeshletTriangleIndexOffset; uint32_t ShowMeshlets; - uint32_t MeshletCount; }; [[vk::push_constant]] @@ -64,8 +65,9 @@ void MeshMain(in uint groupId: SV_GroupID, { uint meshletIndex = groupId; - ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; - ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); + ByteAddressBuffer meshBuffer = ResourceDescriptorHeap[parameters.MeshBuffer]; + + ElemMeshlet meshlet = meshBuffer.Load(parameters.MeshletOffset + meshletIndex * sizeof(ElemMeshlet)); SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); @@ -79,11 +81,8 @@ void MeshMain(in uint groupId: SV_GroupID, float4x4 inverseTransposeWorldMatrix = worldMatrix; - ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - - uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); - Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); + uint vertexIndex = meshBuffer.Load(parameters.MeshletVertexIndexOffset + (meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); + Vertex vertex = meshBuffer.Load(parameters.VertexBufferOffset + vertexIndex * sizeof(Vertex)); //vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), mul(worldMatrix, frameData.ViewProjMatrix)); // NOTE: This calculation is faster because v * M is faster than M * M @@ -94,8 +93,7 @@ void MeshMain(in uint groupId: SV_GroupID, if (groupThreadId < meshlet.TriangleCount) { - ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; - uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); + uint triangleIndex = meshBuffer.Load(parameters.MeshletTriangleIndexOffset + (meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; } diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 43ed3f7b..8abfeba1 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -3,19 +3,22 @@ #include "SampleMath.h" #include "SampleInputsApplication.h" #include "SampleInputsCamera.h" -#include "SampleMesh.h" +#include "SampleSceneLoader.h" +#include "SampleGpuMemory.h" // TODO: Share data between shader and C code typedef struct { uint32_t FrameDataBuffer; - uint32_t VertexBuffer; - uint32_t MeshletBuffer; - uint32_t MeshletVertexIndexBuffer; - uint32_t MeshletTriangleIndexBuffer; - uint32_t ShowMeshlets; + // TODO: Embed that into a buffer + uint32_t MeshBuffer; uint32_t MeshletCount; + uint32_t VertexBufferOffset; + uint32_t MeshletOffset; + uint32_t MeshletVertexIndexOffset; + uint32_t MeshletTriangleIndexOffset; + uint32_t ShowMeshlets; } ShaderParameters; typedef struct @@ -23,19 +26,6 @@ typedef struct SampleMatrix4x4 ViewProjMatrix; } ShaderFrameData; -typedef struct -{ - uint32_t MeshletCount; - ElemGraphicsResource VertexBuffer; - ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; - ElemGraphicsResource MeshletBuffer; - ElemGraphicsResourceDescriptor MeshletBufferReadDescriptor; - ElemGraphicsResource MeshletVertexIndexBuffer; - ElemGraphicsResourceDescriptor MeshletVertexIndexBufferReadDescriptor; - ElemGraphicsResource MeshletTriangleIndexBuffer; - ElemGraphicsResourceDescriptor MeshletTriangleIndexBufferReadDescriptor; -} MeshData; - // TODO: Group common variables into separate structs typedef struct { @@ -43,8 +33,9 @@ typedef struct ElemWindow Window; ElemGraphicsDevice GraphicsDevice; ElemCommandQueue CommandQueue; - ElemGraphicsHeap GraphicsHeap; - uint32_t CurrentHeapOffset; + + SampleGpuMemory GpuMemory; + ElemFence LastExecutionFence; ElemSwapChain SwapChain; ElemGraphicsHeap DepthBufferHeap; @@ -53,12 +44,16 @@ typedef struct ShaderParameters ShaderParameters; SampleInputsApplication InputsApplication; SampleInputsCamera InputsCamera; - MeshData TestMeshData; + SampleSceneData TestSceneData; // TODO: Do we keep that structure here? ShaderFrameData FrameData; - ElemGraphicsResource FrameDataBuffer; - ElemGraphicsResourceDescriptor FrameDataBufferReadDescriptor; + SampleGpuBuffer FrameDataBuffer; } ApplicationPayload; + +typedef struct +{ + SampleInputsCameraState CameraState; +} SavedState; void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); @@ -79,67 +74,13 @@ void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, u applicationPayload->DepthBuffer = ElemCreateGraphicsResource(applicationPayload->DepthBufferHeap, 0, &resourceInfo); } -// TODO: To remove when IOQueues -void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, void* dataPointer, uint32_t sizeInBytes) -{ - // TODO: Alignment should be used with the offset before adding the size of the resource! - ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); - - applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); - *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); - applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; - - *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); - - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(*buffer); - memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); -} - -void LoadMesh(MeshData* meshData, const char* path, ApplicationPayload* applicationPayload) -{ - // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! - // Add a parameter to specify the length we want to read and the offset - ElemDataSpan meshFileData = SampleReadFile(path); - - uint8_t* fileDataPointer = meshFileData.Items; - SampleMeshHeader* meshHeader = (SampleMeshHeader*)fileDataPointer; - - if (meshHeader->FileId[0] == 'M' && meshHeader->FileId[1] == 'E' && meshHeader->FileId[2] == 'S' && meshHeader->FileId[3] == 'H') - { - printf("OK Meshlet Count: %d\n", meshHeader->MeshletCount); - } - - meshData->MeshletCount = meshHeader->MeshletCount; - - CreateAndUploadDataTemp(&meshData->VertexBuffer, &meshData->VertexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->VertexBufferOffset, meshHeader->VertexBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletBuffer, &meshData->MeshletBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletBufferOffset, meshHeader->MeshletBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletVertexIndexBuffer, &meshData->MeshletVertexIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletVertexIndexBufferOffset, meshHeader->MeshletVertexIndexBufferSizeInBytes); - CreateAndUploadDataTemp(&meshData->MeshletTriangleIndexBuffer, &meshData->MeshletTriangleIndexBufferReadDescriptor, applicationPayload, fileDataPointer + meshHeader->MeshletTriangleIndexBufferOffset, meshHeader->MeshletTriangleIndexBufferSizeInBytes); - - applicationPayload->ShaderParameters.VertexBuffer = meshData->VertexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletBuffer = meshData->MeshletBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletVertexIndexBuffer = meshData->MeshletVertexIndexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletTriangleIndexBuffer = meshData->MeshletTriangleIndexBufferReadDescriptor; - applicationPayload->ShaderParameters.MeshletCount = meshData->MeshletCount; -} - -void FreeMesh(MeshData* meshData) -{ - ElemFreeGraphicsResourceDescriptor(meshData->VertexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->VertexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletVertexIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletVertexIndexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(meshData->MeshletTriangleIndexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(meshData->MeshletTriangleIndexBuffer, NULL); -} +// TODO: Copy paste the loading scene code from the common header because it is the first sample to explain how to load a mesh void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 viewProjMatrix) { applicationPayload->FrameData.ViewProjMatrix = viewProjMatrix; - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->FrameDataBuffer); + ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->FrameDataBuffer.Buffer); memcpy(vertexBufferPointer.Items, &applicationPayload->FrameData, sizeof(ShaderFrameData)); } @@ -148,16 +89,8 @@ void InitSample(void* payload) ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; applicationPayload->Window = ElemCreateWindow(&(ElemWindowOptions) { .WindowState = applicationPayload->AppSettings.PreferFullScreen ? ElemWindowState_FullScreen : ElemWindowState_Normal }); - ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); + ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = !applicationPayload->AppSettings.DisableDiagnostics, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); - // TODO: Debug why the AMD integrated GPU is not create at all - ElemGraphicsDeviceInfoSpan devices = ElemGetAvailableGraphicsDevices(); - - for (uint32_t i = 0; i < devices.Length; i++) - { - printf("Device: %s\n", devices.Items[i].DeviceName); - } - applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); @@ -169,15 +102,15 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues // TODO: Having GPU Upload is still annoying 😞 - applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64)); - CreateAndUploadDataTemp(&applicationPayload->FrameDataBuffer, &applicationPayload->FrameDataBufferReadDescriptor, applicationPayload, &applicationPayload->FrameData, sizeof(ShaderFrameData)); - applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBufferReadDescriptor; + applicationPayload->FrameDataBuffer = SampleCreateGpuBufferAndUploadData(&applicationPayload->GpuMemory, &applicationPayload->FrameData, sizeof(ShaderFrameData), "FrameData"); + applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBuffer.ReadDescriptor; CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - LoadMesh(&applicationPayload->TestMeshData, "sponza.scene", applicationPayload); + SampleLoadScene("sponza.scene", &applicationPayload->GpuMemory, &applicationPayload->TestSceneData); - ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader"); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); applicationPayload->GraphicsPipeline = ElemCompileGraphicsPipelineState(applicationPayload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { @@ -205,6 +138,14 @@ void InitSample(void* payload) } SampleStartFrameMeasurement(); + + ElemDataSpan savedStateData = SampleReadFile("SavedState.bin", false); + + if (savedStateData.Length > 0) + { + applicationPayload->InputsCamera.State = ((SavedState*)savedStateData.Items)->CameraState; + applicationPayload->InputsCamera.State.ProjectionMatrix = (SampleMatrix4x4){}; + } } void FreeSample(void* payload) @@ -213,10 +154,9 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); - FreeMesh(&applicationPayload->TestMeshData); + SampleFreeScene(&applicationPayload->TestSceneData); - ElemFreeGraphicsResourceDescriptor(applicationPayload->FrameDataBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->FrameDataBuffer, NULL); + SampleFreeGpuBuffer(&applicationPayload->FrameDataBuffer); ElemFreePipelineState(applicationPayload->GraphicsPipeline); ElemFreeSwapChain(applicationPayload->SwapChain); @@ -225,8 +165,11 @@ void FreeSample(void* payload) ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); - ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); + SampleFreeGpuMemory(&applicationPayload->GpuMemory); ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); + + SavedState savedState = { .CameraState = applicationPayload->InputsCamera.State }; + SampleWriteDataToApplicationFile("SavedState.bin", (ElemDataSpan) { .Items = (uint8_t*)&savedState, .Length = sizeof(SavedState) }, false); } void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) @@ -286,8 +229,26 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void }); ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); - ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + + // TODO: Replace that all here is really bad + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MeshCount; i++) + { + SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[i]; + applicationPayload->ShaderParameters.MeshBuffer = meshData->MeshBuffer.ReadDescriptor; + + for (uint32_t j = 0; j < meshData->MeshPartCount; j++) + { + SampleMeshPartHeader* meshPart = &meshData->MeshParts[j]; + applicationPayload->ShaderParameters.MeshletCount = meshPart->MeshletCount; + applicationPayload->ShaderParameters.VertexBufferOffset = meshPart->VertexBufferOffset; + applicationPayload->ShaderParameters.MeshletOffset = meshPart->MeshletOffset; + applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPart->MeshletVertexIndexOffset; + applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPart->MeshletTriangleIndexOffset; + + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); + ElemDispatchMesh(commandList, meshPart->MeshletCount, 1, 1); + } + } ElemEndRenderPass(commandList); @@ -305,6 +266,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void SampleStartFrameMeasurement(); } + int main(int argc, const char* argv[]) { ApplicationPayload payload = @@ -323,4 +285,5 @@ int main(int argc, const char* argv[]) .FreeHandler = FreeSample, .Payload = &payload }); + } diff --git a/samples/ElementalTools/01-ShaderCompiler/main.c b/samples/ElementalTools/01-ShaderCompiler/main.c index ee5e6261..5d76ff42 100644 --- a/samples/ElementalTools/01-ShaderCompiler/main.c +++ b/samples/ElementalTools/01-ShaderCompiler/main.c @@ -71,18 +71,15 @@ int main(int argc, const char* argv[]) printf("Compiling shader: %s (DebugMode=%d)\n", inputPath, debugMode); ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary(targetApi, targetPlatform, inputPath, &(ElemCompileShaderOptions) { .DebugMode = debugMode }); - - for (uint32_t i = 0; i < compilationResult.Messages.Length; i++) - { - printf("Compil msg (%d): %s\n", compilationResult.Messages.Items[i].Type, compilationResult.Messages.Items[i].Message); - } + + DisplayOutputMessages("ShaderCompiler", compilationResult.Messages); if (compilationResult.HasErrors) { - printf("Error while compiling shader!\n"); return 1; } + // TODO: Add timing informations printf("Writing shader data to: %s\n", outputPath); return SampleWriteDataToFile(outputPath, compilationResult.Data, false); } diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 529d793d..0204a05a 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -1,8 +1,103 @@ #include "ElementalTools.h" #include "SampleUtils.h" -#include "SampleMesh.h" +#include "SampleScene.h" -// TODO: Rename this to scene compiler +// TODO: Restore MeshBuilder to use for hello mesh? + +bool WriteMeshData(FILE* file, ElemSceneMesh mesh) +{ + assert(file); + + SampleMeshHeader meshHeader = + { + .MeshPartCount = mesh.MeshParts.Length + }; + + fwrite(&meshHeader, sizeof(SampleMeshHeader), 1, file); + uint32_t meshPartHeadersOffset = ftell(file); + + SampleMeshPartHeader* meshPartHeaders = (SampleMeshPartHeader*)malloc(sizeof(SampleMeshPartHeader) * mesh.MeshParts.Length); + fwrite(meshPartHeaders, sizeof(SampleMeshPartHeader), mesh.MeshParts.Length, file); + + uint32_t meshPartBufferStartOffset = ftell(file); + + for (uint32_t i = 0; i < mesh.MeshParts.Length; i++) + { + ElemSceneMeshPart* meshPart = &mesh.MeshParts.Items[i]; + + // TODO: Index buffer + ElemBuildMeshletResult result = ElemBuildMeshlets(meshPart->VertexBuffer, NULL); + + DisplayOutputMessages("BuildMeshlets", result.Messages); + + if (result.HasErrors) + { + return false; + } + + SampleMeshPartHeader* meshPartHeader = &meshPartHeaders[i]; + meshPartHeader->MeshletCount = result.Meshlets.Length; + + meshPartHeader->VertexBufferOffset = ftell(file) - meshPartBufferStartOffset; + fwrite(result.VertexBuffer.Data.Items, sizeof(uint8_t), result.VertexBuffer.Data.Length, file); + + meshPartHeader->MeshletOffset = ftell(file) - meshPartBufferStartOffset; + fwrite(result.Meshlets.Items, sizeof(ElemMeshlet), result.Meshlets.Length, file); + + meshPartHeader->MeshletVertexIndexOffset = ftell(file) - meshPartBufferStartOffset; + fwrite(result.MeshletVertexIndexBuffer.Items, sizeof(uint32_t), result.MeshletVertexIndexBuffer.Length, file); + + meshPartHeader->MeshletTriangleIndexOffset = ftell(file) - meshPartBufferStartOffset; + fwrite(result.MeshletTriangleIndexBuffer.Items, sizeof(uint32_t), result.MeshletTriangleIndexBuffer.Length, file); + } + + fseek(file, meshPartHeadersOffset, SEEK_SET); + fwrite(meshPartHeaders, sizeof(SampleMeshPartHeader), mesh.MeshParts.Length, file); + + fseek(file, 0, SEEK_END); + + return true; +} + +bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) +{ + assert(file); + + SampleSceneHeader sceneHeader = + { + .FileId = { 'S', 'C', 'E', 'N', 'E' }, + .MeshCount = scene.Meshes.Length, + }; + + fwrite(&sceneHeader, sizeof(SampleSceneHeader), 1, file); + + // TODO: Get rid of malloc? + + // TODO: change the uint32_t to a struct of offset + size + + SampleDataBlockEntry* meshDataOffsets = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * scene.Meshes.Length); + fwrite(meshDataOffsets, sizeof(SampleDataBlockEntry), scene.Meshes.Length, file); + + double beforeMeshlets = SampleGetTimerValueInMS(); + + for (uint32_t i = 0; i < scene.Meshes.Length; i++) + { + uint32_t dataOffset = ftell(file); + + bool result = WriteMeshData(file, scene.Meshes.Items[i]); + assert(result); + + meshDataOffsets[i] = (SampleDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; + printf("MeshDataOffset: %d, size=%d\n", meshDataOffsets[i].Offset, meshDataOffsets[i].SizeInBytes); + } + + printf("Built meshlets in %.2fs\n", (SampleGetTimerValueInMS() - beforeMeshlets) / 1000.0); + + fseek(file, sizeof(sceneHeader), SEEK_SET); + fwrite(meshDataOffsets, sizeof(SampleDataBlockEntry), scene.Meshes.Length, file); + + return true; +} int main(int argc, const char* argv[]) { @@ -56,7 +151,10 @@ int main(int argc, const char* argv[]) printf("Compiling mesh: %s\n", inputPath); - ElemToolsDataSpan inputData = SampleReadFile(inputPath); + SampleInitTimer(); + double initialTimer = SampleGetTimerValueInMS(); + + ElemToolsDataSpan inputData = SampleReadFile(inputPath, false); if (inputData.Length == 0) { @@ -64,80 +162,21 @@ int main(int argc, const char* argv[]) return 1; } - ElemLoadSceneResult inputScene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .SceneCoordinateSystem = ElemSceneCoordinateSystem_LeftHanded }); - printf("Input mesh vertex Count: %d\n", inputScene.VertexCount); - - ElemBuildMeshletResult result = ElemBuildMeshlets(inputScene.VertexBuffer, NULL); + ElemLoadSceneResult scene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .CoordinateSystem = ElemSceneCoordinateSystem_LeftHanded }); - // TODO: Refactor this into an util function to display messages with proper colors - for (uint32_t i = 0; i < result.Messages.Length; i++) - { - printf("Compil msg (%d): %s\n", result.Messages.Items[i].Type, result.Messages.Items[i].Message); - } + DisplayOutputMessages("LoadScene", scene.Messages); - if (result.HasErrors) + if (scene.HasErrors) { - printf("Error while compiling shader!\n"); return 1; } - // TODO: Good Unit test too! - for (uint32_t i = 0; i < result.Meshlets.Length; i++) - { - ElemMeshlet meshlet = result.Meshlets.Items[i]; - - if (i < result.Meshlets.Length - 1) - { - ElemMeshlet nextMeshlet = result.Meshlets.Items[i + 1]; - - if (meshlet.TriangleOffset + meshlet.TriangleCount - 1 == nextMeshlet.TriangleOffset) - { - printf("Error not last\n"); - } - } - else if (meshlet.TriangleOffset + meshlet.TriangleCount - 1 == result.MeshletTriangleIndexBuffer.Length) - { - printf("ERROR\n"); - } - } - - for (uint32_t i = result.MeshletTriangleIndexBuffer.Length - 10; i < result.MeshletTriangleIndexBuffer.Length + 3; i++) - { - printf("Trig index: %u\n", result.MeshletTriangleIndexBuffer.Items[i]); - } - - for (uint32_t i = 0; i < result.MeshletTriangleIndexBuffer.Length; i++) - { - if (result.MeshletTriangleIndexBuffer.Items[i] == 0) - { - printf("Zero Triangle index at: %d\n", i); - } - } + printf("Loaded mesh in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); printf("Writing Scene data to: %s\n", outputPath); - uint32_t meshletBufferSize = result.Meshlets.Length * sizeof(ElemMeshlet); - uint32_t meshletVertexIndexBufferSize = result.MeshletVertexIndexBuffer.Length * sizeof(uint32_t); - - SampleMeshHeader meshHeader = - { - .FileId = { 'M', 'E', 'S', 'H' }, - .MeshletCount = result.Meshlets.Length, - .MeshletMaxVertexCount = result.MeshletMaxVertexCount, - .MeshletMaxTriangleCount = result.MeshletMaxTriangleCount, - .VertexBufferOffset = sizeof(SampleMeshHeader), - .VertexBufferSizeInBytes = result.VertexBuffer.Data.Length, - .MeshletBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length, - .MeshletBufferSizeInBytes = meshletBufferSize, - .MeshletVertexIndexBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length + meshletBufferSize, - .MeshletVertexIndexBufferSizeInBytes = meshletVertexIndexBufferSize, - .MeshletTriangleIndexBufferOffset = sizeof(SampleMeshHeader) + result.VertexBuffer.Data.Length + meshletBufferSize + meshletVertexIndexBufferSize, - .MeshletTriangleIndexBufferSizeInBytes = result.MeshletTriangleIndexBuffer.Length * sizeof(uint32_t) - }; - - SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)&meshHeader, .Length = sizeof(SampleMeshHeader) }, false); - SampleWriteDataToFile(outputPath, result.VertexBuffer.Data, true); - SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.Meshlets.Items, .Length = meshletBufferSize}, true); - SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.MeshletVertexIndexBuffer.Items, .Length = result.MeshletVertexIndexBuffer.Length * sizeof(uint32_t) }, true); - SampleWriteDataToFile(outputPath, (ElemToolsDataSpan) { .Items = (uint8_t*)result.MeshletTriangleIndexBuffer.Items, .Length = result.MeshletTriangleIndexBuffer.Length * sizeof(uint32_t) }, true); + FILE* file = fopen(outputPath, "wb"); + WriteSceneData(file, scene); + fclose(file); + printf("Scene compiled in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index c0c0f4d0..70f835f3 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -202,6 +202,7 @@ ElemGraphicsDeviceInfo VulkanConstructGraphicsDeviceInfo(MemoryArena memoryArena VulkanDescriptorHeap CreateVulkanDescriptorHeap(VkDevice graphicsDevice, MemoryArena memoryArena, VkDescriptorSetLayout descriptorSetLayout, uint32_t length) { + // TODO: Use VK_DESCRIPTOR BUFFER? VkDescriptorPoolSize poolSizes[] { {VK_DESCRIPTOR_TYPE_MUTABLE_EXT, length } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 9d2ee07a..59e0e4fc 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -231,6 +231,7 @@ ElemGraphicsHeap VulkanCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uin VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocateInfo.allocationSize = sizeInBytes; + // TODO: Still need to investigate this //allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuUploadMemoryTypeIndex; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index 2c276856..c7efca69 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -170,8 +170,6 @@ bool DirectX12CheckGraphicsDeviceCompatibility(ComPtr graphicsDe shaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_8; AssertIfFailed(graphicsDevice->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel))); - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Test"); - // TODO: Update checks if (deviceOptions.ResourceHeapTier == D3D12_RESOURCE_HEAP_TIER_2 && deviceOptions.ResourceBindingTier == D3D12_RESOURCE_BINDING_TIER_3 && diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index a9863420..1af0b5c6 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -120,10 +120,16 @@ typedef struct uint32_t Length; } ElemToolsMessageSpan; +typedef struct +{ + float X, Y, Z; +} ElemVector3; + typedef struct { ElemToolsDataSpan Data; uint32_t VertexSize; + uint32_t VertexCount; // TODO: Add vertex description structure } ElemVertexBuffer; @@ -223,7 +229,8 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph typedef enum { - ElemSceneFormat_Obj = 0, + ElemSceneFormat_Unknown = 0, + ElemSceneFormat_Obj = 1, } ElemSceneFormat; typedef enum @@ -234,15 +241,58 @@ typedef enum typedef struct { - ElemSceneCoordinateSystem SceneCoordinateSystem; + ElemSceneCoordinateSystem CoordinateSystem; // TODO: Add options to specify the vertex format wanted } ElemLoadSceneOptions; typedef struct { - ElemSceneFormat MeshFormat; ElemVertexBuffer VertexBuffer; - uint32_t VertexCount; + ElemUInt32Span IndexBuffer; + // TODO: Material +} ElemSceneMeshPart; + +typedef struct +{ + ElemSceneMeshPart* Items; + uint32_t Length; +} ElemSceneMeshPartSpan; + +typedef struct +{ + ElemSceneMeshPartSpan MeshParts; + // TODO: Bounding volumes +} ElemSceneMesh; + +typedef struct +{ + ElemSceneMesh* Items; + uint32_t Length; +} ElemSceneMeshSpan; + +typedef struct +{ + const char* Name; + int32_t MeshIndex; + ElemVector3 Rotation; + ElemVector3 Translation; + // TODO: Children +} ElemSceneNode; + +typedef struct +{ + ElemSceneNode* Items; + uint32_t Length; +} ElemSceneNodeSpan; + +typedef struct +{ + ElemSceneFormat SceneFormat; + ElemSceneCoordinateSystem CoordinateSystem; + + ElemSceneMeshSpan Meshes; + ElemSceneNodeSpan Nodes; + ElemToolsMessageSpan Messages; bool HasErrors; } ElemLoadSceneResult; @@ -264,6 +314,7 @@ typedef struct typedef struct { + // TODO: Add base vertex uint32_t VertexIndexOffset; uint32_t VertexIndexCount; uint32_t TriangleOffset; diff --git a/src/ElementalTools/Meshes/MeshletBuilder.cpp b/src/ElementalTools/Meshes/MeshletBuilder.cpp index 4b8a1aab..a79714b4 100644 --- a/src/ElementalTools/Meshes/MeshletBuilder.cpp +++ b/src/ElementalTools/Meshes/MeshletBuilder.cpp @@ -22,6 +22,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto stackMemoryArena = SystemGetStackMemoryArena(); + // TODO: Take into account the index buffer if present auto indexCount = vertexBuffer.Data.Length / vertexBuffer.VertexSize; auto vertexRemap = SystemPushArrayZero(stackMemoryArena, indexCount); @@ -117,7 +118,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf { .MeshletMaxVertexCount = meshletMaxVertexCount, .MeshletMaxTriangleCount = meshletMaxTriangleCount, - .VertexBuffer = { .Data = { .Items = vertexList.Pointer, .Length = (uint32_t)vertexList.Length }, .VertexSize = vertexBuffer.VertexSize }, + .VertexBuffer = { .Data = { .Items = vertexList.Pointer, .Length = (uint32_t)vertexList.Length }, .VertexSize = vertexBuffer.VertexSize, .VertexCount = vertexBuffer.VertexCount }, .Meshlets = { .Items = meshletList.Pointer, .Length = (uint32_t)meshletList.Length }, .MeshletVertexIndexBuffer = { .Items = meshletVertexIndexList.Pointer, .Length = meshletVertexIndexCount }, .MeshletTriangleIndexBuffer = { .Items = meshletTriangleIndexList.Pointer, .Length = meshletTriangleIndexCount } diff --git a/src/ElementalTools/SceneLoading/SceneLoader.cpp b/src/ElementalTools/SceneLoading/SceneLoader.cpp index c0a240c0..8ef04b1c 100644 --- a/src/ElementalTools/SceneLoading/SceneLoader.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoader.cpp @@ -1,209 +1,68 @@ -#include "ToolsUtils.h" #include "ElementalTools.h" #include "SystemMemory.h" -#include "SystemFunctions.h" +#include "SceneLoaderObj.cpp" // TODO: Do one for each thread static MemoryArena SceneLoaderMemoryArena; -struct ObjLoaderFileData -{ - ReadOnlySpan FileData; - uint32_t CurrentOffset; -}; - -void* FastObjFileOpen(const char* path, void* userData) -{ - auto objFileData = LoadFileData(path); - - if (objFileData.Length == 0) - { - return nullptr; - } - - auto objLoaderFileData = SystemPushStruct(*(MemoryArena*)userData); - objLoaderFileData->CurrentOffset = 0; - objLoaderFileData->FileData = objFileData; - - return objLoaderFileData; -} - -void FastObjFileClose(void* file, void* userData) -{ -} - -size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userData) +void InitSceneLoaderMemoryArena() { - SystemAssert(file); - - auto objLoaderFileData = (ObjLoaderFileData*)file; - auto sourceSpan = objLoaderFileData->FileData; - auto destinationSpan = Span((uint8_t*)destination, bytes); - - auto readLength = SystemMin(sourceSpan.Length - objLoaderFileData->CurrentOffset, bytes); - - if (readLength > 0) + if (SceneLoaderMemoryArena.Storage == nullptr) { - SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); + SceneLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); } - objLoaderFileData->CurrentOffset += readLength; - return readLength; + SystemClearMemoryArena(SceneLoaderMemoryArena); } -unsigned long FastObjFileSize(void* file, void* userData) +MemoryArena GetSceneLoaderMemoryArena() { - SystemAssert(file); - - auto objLoaderFileData = (ObjLoaderFileData*)file; - return objLoaderFileData->FileData.Length; + return SceneLoaderMemoryArena; } -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) +ElemSceneFormat GetSceneFormatFromPath(const char* path) { - // TODO: Check what to include - auto currentVertexBufferPointer = *vertexBufferPointer; - - if (objVertex.p) - { - for (uint32_t j = 0; j < 3; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; - } - - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) - { - ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; - } - - currentVertexBufferPointer += 3 * sizeof(float); - } - - if (objVertex.n) - { - for (uint32_t j = 0; j < 3; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->normals[3 * objVertex.n + j]; - } - - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) - { - ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; - } - - currentVertexBufferPointer += 3 * sizeof(float); - } - - if (objVertex.t) - { - for (uint32_t j = 0; j < 2; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->texcoords[2 * objVertex.t + j]; - } - } - - // TODO: Force the texture coordinates for now - currentVertexBufferPointer += 2 * sizeof(float); - *vertexBufferPointer = currentVertexBufferPointer; -} - -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ElemLoadSceneOptions* options) -{ - for (uint32_t i = 0; i < objMesh->object_count; i++) - { - auto object = objMesh->objects[i]; - printf("Object (Node): %s\n", object.name); - - /* - for (uint32_t j = 0; j < object.face_count; j++) - { - auto face = objMesh->face_materials - auto group = objMesh->face_materials[j]; - printf("Group: %s\n", group.name); - }*/ - } - - // TODO: Handle sub objects - ElemSceneCoordinateSystem coordinateSystem = ElemSceneCoordinateSystem_LeftHanded; - - if (options != nullptr) - { - coordinateSystem = options->SceneCoordinateSystem; - } - - auto positionSize = sizeof(float) * 3; - auto normalSize = sizeof(float) * 3; - auto textureCoordinatesSize = sizeof(float) * 2; - auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; - - // TODO: Temporary - auto realVertexSize = maxVertexSize; + auto pathSpan = ReadOnlySpan(path); + auto stackMemoryArena = SystemGetStackMemoryArena(); - auto vertexBuffer = SystemPushArray(memoryArena, objMesh->index_count * maxVertexSize); - auto currentVertexBufferPointer = vertexBuffer.Pointer; + auto lastIndex = SystemLastIndexOf(path, '.'); + SystemAssert(lastIndex != -1); - SystemAssert((objMesh->index_count % 3) == 0); + auto extension = SystemPushArray(stackMemoryArena, pathSpan.Length - lastIndex); + SystemCopyBuffer(extension, pathSpan.Slice(lastIndex + 1)); - for (uint32_t i = 0; i < objMesh->index_count; i += 3) + if (SystemFindSubString(extension, "obj") != -1) { - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) - { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); - } - else - { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); - } + return ElemSceneFormat_Obj; } - return - { - .Data = { .Items = vertexBuffer.Pointer, .Length = objMesh->index_count * (uint32_t)realVertexSize }, - .VertexSize = (uint32_t)realVertexSize - }; + return ElemSceneFormat_Unknown; } -void InitSceneLoaderMemoryArena() -{ - if (SceneLoaderMemoryArena.Storage == nullptr) - { - SceneLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); - } - - SystemClearMemoryArena(SceneLoaderMemoryArena); -} - -// TODO: Split object loaders into multiple files ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadSceneOptions* options) { InitSceneLoaderMemoryArena(); - // TODO: To output the results, we should use a separate memory arena for each modules - - auto stackMemoryArena = SystemGetStackMemoryArena(); - auto hasErrors = false; + ElemLoadSceneOptions loadSceneOptions = {}; - auto callbacks = fastObjCallbacks + if (options) { - .file_open = FastObjFileOpen, - .file_close = FastObjFileClose, - .file_read = FastObjFileRead, - .file_size = FastObjFileSize - }; - - auto mesh = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); + loadSceneOptions = *options; + } - // TODO: Support instances and meshes - auto vertexBuffer = ConstructObjVertexBuffer(SceneLoaderMemoryArena, mesh, options); + auto sceneFormat = GetSceneFormatFromPath(path); - return + // TODO: Refactor that with array entries and function pointer + switch (sceneFormat) { - .VertexBuffer = vertexBuffer, - .VertexCount = mesh->index_count, - .HasErrors = hasErrors + case ElemSceneFormat_Obj: + return LoadObjScene(path, &loadSceneOptions); + + default: + return + { + .Messages = ConstructErrorMessageSpan(GetSceneLoaderMemoryArena(), "Scene format is not supported."), + .HasErrors = true + }; }; } diff --git a/src/ElementalTools/SceneLoading/SceneLoader.h b/src/ElementalTools/SceneLoading/SceneLoader.h new file mode 100644 index 00000000..2a84f81d --- /dev/null +++ b/src/ElementalTools/SceneLoading/SceneLoader.h @@ -0,0 +1,5 @@ +#pragma once + +#include "SystemMemory.h" + +MemoryArena GetSceneLoaderMemoryArena(); diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp new file mode 100644 index 00000000..bcccc54c --- /dev/null +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -0,0 +1,254 @@ +#include "SceneLoader.h" +#include "ToolsUtils.h" +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + +struct ObjLoaderFileData +{ + ReadOnlySpan FileData; + uint32_t CurrentOffset; +}; + +struct ObjMeshPartInfo +{ + uint32_t IndexOffset; + uint32_t IndexCount; +}; + +void* FastObjFileOpen(const char* path, void* userData) +{ + auto objFileData = LoadFileData(path); + + if (objFileData.Length == 0) + { + return nullptr; + } + + auto objLoaderFileData = SystemPushStruct(*(MemoryArena*)userData); + objLoaderFileData->CurrentOffset = 0; + objLoaderFileData->FileData = objFileData; + + return objLoaderFileData; +} + +void FastObjFileClose(void* file, void* userData) +{ +} + +size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userData) +{ + SystemAssert(file); + + auto objLoaderFileData = (ObjLoaderFileData*)file; + auto sourceSpan = objLoaderFileData->FileData; + auto destinationSpan = Span((uint8_t*)destination, bytes); + + auto readLength = SystemMin(sourceSpan.Length - objLoaderFileData->CurrentOffset, bytes); + + if (readLength > 0) + { + SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); + } + + objLoaderFileData->CurrentOffset += readLength; + return readLength; +} + +unsigned long FastObjFileSize(void* file, void* userData) +{ + SystemAssert(file); + + auto objLoaderFileData = (ObjLoaderFileData*)file; + return objLoaderFileData->FileData.Length; +} + +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) +{ + // TODO: Check what to include + auto currentVertexBufferPointer = *vertexBufferPointer; + + if (objVertex.p) + { + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; + } + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + + currentVertexBufferPointer += 3 * sizeof(float); + } + + if (objVertex.n) + { + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->normals[3 * objVertex.n + j]; + } + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + + currentVertexBufferPointer += 3 * sizeof(float); + } + + if (objVertex.t) + { + for (uint32_t j = 0; j < 2; j++) + { + ((float*)currentVertexBufferPointer)[j] = objMesh->texcoords[2 * objVertex.t + j]; + } + } + + // TODO: Force the texture coordinates for now + currentVertexBufferPointer += 2 * sizeof(float); + *vertexBufferPointer = currentVertexBufferPointer; +} + +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPartInfo* meshPartInfo, const ElemLoadSceneOptions* options) +{ + SystemAssert(options); + auto coordinateSystem = options->CoordinateSystem; + + // TODO: Config of the vertex components to load + auto positionSize = sizeof(float) * 3; + auto normalSize = sizeof(float) * 3; + auto textureCoordinatesSize = sizeof(float) * 2; + auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; + + // TODO: Temporary + auto realVertexSize = maxVertexSize; + + auto vertexBuffer = SystemPushArray(memoryArena, meshPartInfo->IndexCount * maxVertexSize); + auto currentVertexBufferPointer = vertexBuffer.Pointer; + + for (uint32_t i = meshPartInfo->IndexOffset; i < meshPartInfo->IndexOffset + meshPartInfo->IndexCount; i += 3) + { + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); + } + else + { + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); + } + } + + return + { + .Data = { .Items = vertexBuffer.Pointer, .Length = (uint32_t)vertexBuffer.Length }, + .VertexSize = (uint32_t)realVertexSize, + .VertexCount = meshPartInfo->IndexCount + }; +} + +ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto hasErrors = false; + + auto callbacks = fastObjCallbacks + { + .file_open = FastObjFileOpen, + .file_close = FastObjFileClose, + .file_read = FastObjFileRead, + .file_size = FastObjFileSize + }; + + auto objFileData = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); + auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); + + auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); + auto meshPartInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); + + for (uint32_t i = 0; i < objFileData->object_count; i++) + { + auto mesh = &meshes[i]; + auto objectData = &objFileData->objects[i]; + + if (objectData->face_count == 0) + { + printf("Invalid object\n"); + continue; + } + + printf("Object/Mesh: %s\n", objectData->name); + + auto currentFaceStart = objectData->face_offset; + auto currentFaceEnd = objectData->face_offset + objectData->face_count; + + auto indexOffset = 0u; + + for (uint32_t j = 0; j < currentFaceStart; j++) + { + indexOffset += objFileData->face_vertices[j]; + } + + auto currentMaterial = objFileData->face_materials[currentFaceStart]; + printf(" MeshPart (Material: %d)\n", currentMaterial); + + auto meshPartCount = 0u; + + auto meshPartInfo = &meshPartInfos[meshPartCount++]; + *meshPartInfo = { .IndexOffset = indexOffset }; + + for (uint32_t j = currentFaceStart; j < currentFaceEnd; j++) + { + auto faceMaterial = objFileData->face_materials[j]; + int faceVertexCount = objFileData->face_vertices[j]; + + if (faceVertexCount != 3) + { + return + { + .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Obj mesh loader only support triangles."), + .HasErrors = true + }; + } + + if (faceMaterial != currentMaterial) + { + currentMaterial = faceMaterial; + + printf(" MeshPart (Material: %d)\n", currentMaterial); + + auto meshPartIndexOffset = indexOffset + meshPartInfo->IndexCount; + + meshPartInfo = &meshPartInfos[meshPartCount++]; + *meshPartInfo = { .IndexOffset = meshPartIndexOffset }; + } + + meshPartInfo->IndexCount += faceVertexCount; + } + + auto meshParts = SystemPushArray(sceneLoaderMemoryArena, meshPartCount); + + for (uint32_t i = 0; i < meshPartCount; i++) + { + auto meshPart = &meshParts[i]; + auto meshPartInfo = &meshPartInfos[i]; + + meshPart->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, objFileData, meshPartInfo, options); + } + + mesh->MeshParts = { .Items = meshParts.Pointer, .Length = (uint32_t)meshParts.Length }; + } + + return + { + .SceneFormat = ElemSceneFormat_Obj, + .CoordinateSystem = options->CoordinateSystem, + .Meshes = { .Items = meshes.Pointer, .Length = (uint32_t)meshes.Length }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp index 64d94bbb..efcf68e7 100644 --- a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp @@ -103,6 +103,8 @@ ShaderType GetShaderTypeEnum(DxilShaderKind shaderKind) ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOnlySpan target, ElemToolsGraphicsApi targetApi, ReadOnlySpan entryPoint, const ElemCompileShaderOptions* options) { + // TODO: Allow passing the pass to have a correct name in the messages + auto stackMemoryArena = SystemGetStackMemoryArena(); ComPtr compiler; @@ -127,6 +129,9 @@ ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn parameters[parameterIndex++] = L"-spirv"; parameters[parameterIndex++] = L"-fspv-target-env=vulkan1.3"; parameters[parameterIndex++] = L"-fvk-use-dx-layout"; + // TODO: Add an options for this + // TODO: To review + parameters[parameterIndex++] = L"-fspv-use-legacy-buffer-matrix-order"; } parameters[parameterIndex++] = L"-HV"; @@ -205,8 +210,6 @@ ElemShaderCompilationResult DirectXShaderCompilerCompileShader(MemoryArena memor ComPtr shaderByteCodeComPtr; AssertIfFailed(dxilCompileResult->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&shaderByteCodeComPtr), nullptr)); - auto shaderByteCode = Span((uint8_t*)shaderByteCodeComPtr->GetBufferPointer(), shaderByteCodeComPtr->GetBufferSize()); - ComPtr dxcUtils; AssertIfFailed(directXShaderCompilerCreateInstanceFunction(CLSID_DxcUtils, IID_PPV_ARGS(&dxcUtils))); diff --git a/src/ElementalTools/Shaders/ShaderCompiler.cpp b/src/ElementalTools/Shaders/ShaderCompiler.cpp index 1deae285..277c943e 100644 --- a/src/ElementalTools/Shaders/ShaderCompiler.cpp +++ b/src/ElementalTools/Shaders/ShaderCompiler.cpp @@ -201,17 +201,9 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph if (!FindShaderCompilerChain(shaderLanguage, targetLanguage, compilerSteps, &level)) { - auto messageItem = SystemPushStruct(stackMemoryArena); - messageItem->Type = ElemToolsMessageType_Error; - messageItem->Message = "Cannot find a compatible shader compilers chain."; - return { - .Messages = - { - .Items = messageItem, - .Length = 1 - }, + .Messages = ConstructErrorMessageSpan(ShaderCompilerMemoryArena, "Cannot find a compatible shader compilers chain."), .HasErrors = true }; } @@ -222,17 +214,9 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph if (stepSourceData.Length == 0) { - auto messageItem = SystemPushStruct(stackMemoryArena); - messageItem->Type = ElemToolsMessageType_Error; - messageItem->Message = "Cannot read input file."; - return { - .Messages = - { - .Items = messageItem, - .Length = 1 - }, + .Messages = ConstructErrorMessageSpan(ShaderCompilerMemoryArena, "Cannot read input file."), .HasErrors = true }; } @@ -269,7 +253,6 @@ ElemToolsAPI ElemShaderCompilationResult ElemCompileShaderLibrary(ElemToolsGraph ResetLoadFileDataMemory(); - // TODO: output the values in a separate arena return { .Data = diff --git a/src/ElementalTools/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp index 74d82265..4c080b72 100644 --- a/src/ElementalTools/ToolsUtils.cpp +++ b/src/ElementalTools/ToolsUtils.cpp @@ -45,3 +45,16 @@ void ResetLoadFileDataMemory() { SystemClearMemoryArena(FileIOMemoryArena); } + +ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const char* errorMessage) +{ + auto messageItem = SystemPushStruct(memoryArena); + messageItem->Type = ElemToolsMessageType_Error; + messageItem->Message = errorMessage; + + return + { + .Items = messageItem, + .Length = 1 + }; +} diff --git a/src/ElementalTools/ToolsUtils.h b/src/ElementalTools/ToolsUtils.h index 769dfe84..1cad9b9b 100644 --- a/src/ElementalTools/ToolsUtils.h +++ b/src/ElementalTools/ToolsUtils.h @@ -1,6 +1,10 @@ #pragma once +#include "ElementalTools.h" +#include "SystemMemory.h" #include "SystemSpan.h" ReadOnlySpan LoadFileData(const char* path); void ResetLoadFileDataMemory(); + +ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const char* errorMessage); diff --git a/tests/ToolsTests/MeshBuilderTests.cpp b/tests/ToolsTests/MeshBuilderTests.cpp index a427f2a5..c7c6bae4 100644 --- a/tests/ToolsTests/MeshBuilderTests.cpp +++ b/tests/ToolsTests/MeshBuilderTests.cpp @@ -38,7 +38,8 @@ ElemVertexBuffer TestBuildVertexBuffer(TestMeshVertex* destination, uint32_t cou return { .Data = { .Items = (uint8_t*)destination, .Length = (uint32_t)(count * sizeof(TestMeshVertex)) }, - .VertexSize = sizeof(TestMeshVertex) + .VertexSize = sizeof(TestMeshVertex), + .VertexCount = count }; } @@ -102,7 +103,8 @@ UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) // Assert ASSERT_FALSE(result.HasErrors); - ASSERT_EQ_MSG(result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize, vertexCount - 3, "Vertex Count is not correct."); + ASSERT_EQ_MSG(result.VertexBuffer.Data.Length / result.VertexBuffer.VertexSize, vertexCount - 3, "Vertex Count from vertexbuffer length is not correct."); + ASSERT_EQ_MSG(result.VertexBuffer.VertexCount, vertexCount - 3, "Vertex Count is not correct."); } UTEST(MeshBuilder, CheckMeshletVertexIndexBuffer) diff --git a/tests/ToolsTests/SceneLoaderTests.cpp b/tests/ToolsTests/SceneLoaderTests.cpp index 285c28ff..783814b8 100644 --- a/tests/ToolsTests/SceneLoaderTests.cpp +++ b/tests/ToolsTests/SceneLoaderTests.cpp @@ -47,6 +47,9 @@ auto cubeObjSceneSource = R"(o Cube struct SceneLoader_LoadScene { const char* Path; + ElemSceneFormat SceneFormat; + uint32_t ExpectedNodeCount; + uint32_t ExpectedMeshCount; uint32_t ExpectedVertexCount; }; @@ -62,15 +65,25 @@ UTEST_F_TEARDOWN(SceneLoader_LoadScene) // Assert ASSERT_FALSE(result.HasErrors); - ASSERT_EQ_MSG(result.VertexCount, utest_fixture->ExpectedVertexCount, "LoadScene vertex count is not correct."); - ASSERT_GT_MSG(result.VertexBuffer.VertexSize, 0u, "Vertex size must be greater than 0."); - ASSERT_GT_MSG(result.VertexBuffer.Data.Length, 0u, "VertexBuffer size must be greater than 0."); + + ASSERT_EQ_MSG(result.SceneFormat, utest_fixture->SceneFormat, "Scene format is invalid."); + + // TODO: + ASSERT_EQ_MSG(result.Nodes.Length, utest_fixture->ExpectedNodeCount, "Node count is not correct."); + ASSERT_EQ_MSG(result.Meshes.Length, utest_fixture->ExpectedMeshCount, "Mesh count is not correct."); + + ElemSceneMesh mesh = result.Meshes.Items[0]; + ElemSceneMeshPart meshPart = mesh.MeshParts.Items[0]; + + ASSERT_EQ_MSG(meshPart.VertexBuffer.VertexCount, utest_fixture->ExpectedVertexCount, "MeshPart vertex count is not correct."); + ASSERT_GT_MSG(meshPart.VertexBuffer.VertexSize, 0u, "Vertex size must be greater than 0."); + ASSERT_GT_MSG(meshPart.VertexBuffer.Data.Length, 0u, "VertexBuffer size must be greater than 0."); bool isEmpty = true; - for (uint32_t i = 0; i < result.VertexBuffer.Data.Length; i++) + for (uint32_t i = 0; i < meshPart.VertexBuffer.Data.Length; i++) { - if (result.VertexBuffer.Data.Items[i] != 0) + if (meshPart.VertexBuffer.Data.Items[i] != 0) { isEmpty = false; break; @@ -85,5 +98,10 @@ UTEST_F_TEARDOWN(SceneLoader_LoadScene) UTEST_F(SceneLoader_LoadScene, Obj) { utest_fixture->Path = "Cube.obj"; + utest_fixture->SceneFormat = ElemSceneFormat_Obj; + utest_fixture->ExpectedMeshCount = 1; + utest_fixture->ExpectedNodeCount = 1; + + // TODO: To change utest_fixture->ExpectedVertexCount = 36; } diff --git a/tests/ToolsTests/ToolsTests.h b/tests/ToolsTests/ToolsTests.h index 57a2cdb0..1081db26 100644 --- a/tests/ToolsTests/ToolsTests.h +++ b/tests/ToolsTests/ToolsTests.h @@ -1,5 +1,4 @@ #pragma once -#define _CRT_SECURE_NO_WARNINGS #include "ElementalTools.h" #include "utest.h" From d6b191f416cf92fa55b627382c5582988c3de184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 24 Dec 2024 15:57:50 +0100 Subject: [PATCH 41/93] Fix Apple bugs --- samples/Common/SampleInputsCamera.h | 5 +++-- samples/Common/SampleMath.h | 2 +- .../GameController/GCKeyboardInput.hpp | 15 ++++++++++--- .../Apple/Graphics/MetalCommandList.cpp | 1 + .../Apple/Graphics/MetalCommandList.h | 1 + src/Elemental/Apple/Graphics/MetalShader.cpp | 21 ++++++++++++------- .../Apple/Graphics/MetalSwapChain.cpp | 10 ++++++++- src/Elemental/Apple/Inputs.cpp | 6 ++++++ 8 files changed, 47 insertions(+), 14 deletions(-) diff --git a/samples/Common/SampleInputsCamera.h b/samples/Common/SampleInputsCamera.h index ff121870..7bb2a108 100644 --- a/samples/Common/SampleInputsCamera.h +++ b/samples/Common/SampleInputsCamera.h @@ -87,8 +87,9 @@ void SampleInputsCameraInit(SampleInputsCamera* inputs) SampleRegisterInputActionBinding(&inputs->InputActionBindings, ElemInputId_KeySpacebar, 0, SampleInputActionBindingType_Value, &inputs->InputActions.Action); // TODO: Temporary Init code for now - inputs->State.Camera.Position = (SampleVector3) { 0.0f, 0.0f, -0.0f }; - inputs->State.Camera.Rotation = V3Zero; + SampleCamera* camera = &(inputs->State.Camera); + camera->Position = (SampleVector3) { 0.0f, 100.0f, -10.0f }; + camera->Rotation = (SampleVector3) { 0.0f, 1.2f, 0 }; // TODO: Allow the reset of camera to the passed initial position when we press a button } diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 30ce9a0e..8da3fe70 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -11,7 +11,7 @@ // TODO: Review the angle functions, we are using left handed coordinate system. So positive rotation should // be in clockwise order -inline float SamplePow2f(float value) +float SamplePow2f(float value) { return value * value; } diff --git a/src/Elemental/Apple/Frameworks/GameController/GCKeyboardInput.hpp b/src/Elemental/Apple/Frameworks/GameController/GCKeyboardInput.hpp index 550795a6..caebf310 100644 --- a/src/Elemental/Apple/Frameworks/GameController/GCKeyboardInput.hpp +++ b/src/Elemental/Apple/Frameworks/GameController/GCKeyboardInput.hpp @@ -8,6 +8,7 @@ namespace GC { + // BUG: The list is wrong! _NS_ENUM(NS::UInteger, KeyCode) { KeyA = 4, @@ -85,15 +86,15 @@ namespace GC F19, F20, PrintScreen, - ScrollLock, + /*ScrollLock, Pause, Insert, Home, PageUp, DeleteForward, End, - PageDown, - RightArrow, + PageDown,*/ + RightArrow = 79, LeftArrow, DownArrow, UpArrow, @@ -144,6 +145,14 @@ namespace GC RightShift, RightAlt, RightGui, + ScrollLock, + Pause, + Insert, + Home, + PageUp, + DeleteForward, + End, + PageDown, }; class KeyboardInput; diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index b0349095..edc9dabb 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -75,6 +75,7 @@ void ResetMetalCommandEncoder(ElemCommandList commandList) commandListData->CommandEncoder->endEncoding(); commandListData->CommandEncoder.reset(); commandListData->CommandEncoderType = MetalCommandEncoderType_None; + commandListData->ArgumentBufferBound = false; } } diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.h b/src/Elemental/Apple/Graphics/MetalCommandList.h index 43e801c6..7c783b29 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.h +++ b/src/Elemental/Apple/Graphics/MetalCommandList.h @@ -44,6 +44,7 @@ struct MetalCommandListData ElemPipelineState PipelineState; bool IsCommitted; ResourceBarrierPool ResourceBarrierPool; + bool ArgumentBufferBound; }; struct MetalCommandListDataFull diff --git a/src/Elemental/Apple/Graphics/MetalShader.cpp b/src/Elemental/Apple/Graphics/MetalShader.cpp index bfcd23ff..ac01a8d2 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.cpp +++ b/src/Elemental/Apple/Graphics/MetalShader.cpp @@ -350,7 +350,6 @@ ElemPipelineState MetalCompileGraphicsPipelineState(ElemGraphicsDevice graphicsD auto cullMode = ConvertToMetalCullMode(parameters->CullMode); auto fillMode = parameters->FillMode == ElemGraphicsFillMode_Solid ? MTL::TriangleFillModeFill : MTL::TriangleFillModeLines; - MetalShaderMetaData amplificationShaderMetaData = {}; MetalShaderMetaData meshShaderMetaData = {}; if (parameters->MeshShaderFunction) @@ -542,6 +541,9 @@ void MetalBindPipelineState(ElemCommandList commandList, ElemPipelineState pipel void MetalPushPipelineStateConstants(ElemCommandList commandList, uint32_t offsetInBytes, ElemDataSpan data) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto pushData = SystemDuplicateBuffer(stackMemoryArena, ReadOnlySpan(data.Items, data.Length)); + SystemAssert(commandList != ELEM_HANDLE_NULL); auto commandListData = GetMetalCommandListData(commandList); @@ -556,18 +558,23 @@ void MetalPushPipelineStateConstants(ElemCommandList commandList, uint32_t offse // TODO: Do we need to check if the shader stage is bound? // TODO: compute offset - // HACK: For the oment we set the slot 2 because it is the global one for bindless + // HACK: For the moment we set the slot 2 because it is the global one for bindless - renderCommandEncoder->setMeshBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); - renderCommandEncoder->setMeshBytes(data.Items, data.Length, 2); + if (!commandListData->ArgumentBufferBound) + { + renderCommandEncoder->setMeshBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + renderCommandEncoder->setFragmentBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + commandListData->ArgumentBufferBound = true; + } - renderCommandEncoder->setFragmentBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); - renderCommandEncoder->setFragmentBytes(data.Items, data.Length, 2); + renderCommandEncoder->setMeshBytes(pushData.Pointer, pushData.Length, 2); + renderCommandEncoder->setFragmentBytes(pushData.Pointer, pushData.Length, 2); } else if (commandListData->CommandEncoderType == MetalCommandEncoderType_Compute) { + // TODO: We need to bind the argument buffer too here auto computeCommandEncoder = (MTL::ComputeCommandEncoder*)commandListData->CommandEncoder.get(); - computeCommandEncoder->setBytes(data.Items, data.Length, 2); + computeCommandEncoder->setBytes(pushData.Pointer, pushData.Length, 2); } } diff --git a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp index f344363f..1a274bf3 100644 --- a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp +++ b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp @@ -265,9 +265,10 @@ void MetalDisplayLinkHandler::metalDisplayLinkNeedsUpdate(CA::MetalDisplayLink* auto nextPresentTimestampInSeconds = update->targetPresentationTimestamp() - swapChainData->CreationTimestamp; - auto windowSize = ElemGetWindowRenderSize(swapChainData->Window); auto sizeChanged = false; + auto windowSize = ElemGetWindowRenderSize(swapChainData->Window); + if (windowSize.Width > 0 && windowSize.Height > 0 && (windowSize.Width != swapChainData->Width || windowSize.Height != swapChainData->Height)) { ResizeMetalSwapChain(_swapChain, windowSize.Width, windowSize.Height, windowSize.UIScale); @@ -283,6 +284,13 @@ void MetalDisplayLinkHandler::metalDisplayLinkNeedsUpdate(CA::MetalDisplayLink* return; } + // BUG: On iOS, the drawable will have his size change on next update + if (swapChainData->BackBufferDrawable->texture()->width() != swapChainData->Width || swapChainData->BackBufferDrawable->texture()->height() != swapChainData->Height) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Error size swapchain"); + return; + } + // TODO: Can we do something better than juste creating/destroying each time? auto backBufferTexture = CreateMetalGraphicsResourceFromResource(swapChainData->GraphicsDevice, ElemGraphicsResourceType_Texture2D, ElemGraphicsResourceUsage_RenderTarget, NS::RetainPtr(swapChainData->BackBufferDrawable->texture()), true); diff --git a/src/Elemental/Apple/Inputs.cpp b/src/Elemental/Apple/Inputs.cpp index d4078ce1..714ce910 100644 --- a/src/Elemental/Apple/Inputs.cpp +++ b/src/Elemental/Apple/Inputs.cpp @@ -49,6 +49,12 @@ void RemoveAppleInputDevice(void* device) ElemInputId GetAppleInputIdFromKeyCode(GC::KeyCode keyCode) { + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Test: %d", keyCode); + if (keyCode == GC::KeyCode::UpArrow) + { + SystemLogDebugMessage(ElemLogMessageCategory_Inputs, "Test"); + } + switch (keyCode) { case GC::KeyCode::GraveAccentAndTilde: return ElemInputId_KeyTilde; From f34fdcd0f052d87bff87ecf0d97906fa2895155f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 26 Dec 2024 09:00:21 +0100 Subject: [PATCH 42/93] Mesh loading buffer improvement --- samples/Common/SampleScene.h | 7 +- samples/Common/SampleSceneLoader.h | 68 +++++++++++++------ .../99-Renderer/Data/RenderMesh.hlsl | 10 ++- samples/Elemental/99-Renderer/main.c | 32 +++++---- .../ElementalTools/02-SceneCompiler/main.c | 42 ++++++------ src/Elemental/Elemental.h | 3 + src/ElementalTools/ElementalTools.h | 8 +-- .../SceneLoading/SceneLoaderObj.cpp | 14 ++-- tests/ToolsTests/SceneLoaderTests.cpp | 4 +- 9 files changed, 116 insertions(+), 72 deletions(-) diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h index 6fa5fc82..372b1309 100644 --- a/samples/Common/SampleScene.h +++ b/samples/Common/SampleScene.h @@ -11,9 +11,12 @@ typedef struct typedef struct { // TODO: Vertex size, etc. - uint32_t MeshPartCount; + uint32_t MeshPrimitiveCount; + uint32_t MeshBufferOffset; + uint32_t MeshBufferSizeInBytes; } SampleMeshHeader; +// TODO: Rename to mesh primitive? typedef struct { uint32_t MeshletCount; @@ -21,7 +24,7 @@ typedef struct uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; -} SampleMeshPartHeader; +} SampleMeshPrimitiveHeader; typedef struct { diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 05b91a8c..0ce02207 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -7,8 +7,9 @@ typedef struct { - uint32_t MeshPartCount; - SampleMeshPartHeader* MeshParts; + const char* Path; + SampleMeshHeader MeshHeader; + SampleMeshPrimitiveHeader* MeshPrimitives; SampleGpuBuffer MeshBuffer; } SampleMeshData; @@ -18,36 +19,60 @@ typedef struct SampleMeshData* Meshes; } SampleSceneData; -void SampleLoadMesh(FILE* file, const SampleDataBlockEntry* datablock, SampleGpuMemory* gpuMemory, SampleMeshData* meshData, const char* debugName) +void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) { - fseek(file, datablock->Offset, SEEK_SET); + *meshData = (SampleMeshData){}; + meshData->Path = path; - SampleMeshHeader meshHeader; - fread(&meshHeader, sizeof(SampleMeshHeader), 1, file); + FILE* file = SampleOpenFile(path, true); + assert(file); - meshData->MeshPartCount = meshHeader.MeshPartCount; - meshData->MeshParts = (SampleMeshPartHeader*)malloc(sizeof(SampleMeshPartHeader) * meshHeader.MeshPartCount); - fread(meshData->MeshParts, sizeof(SampleMeshPartHeader), meshHeader.MeshPartCount, file); + if (offset > 0) + { + fseek(file, offset, SEEK_SET); + } + + fread(&meshData->MeshHeader, sizeof(SampleMeshHeader), 1, file); - uint32_t currentOffset = ftell(file) - datablock->Offset; - uint32_t meshBufferSizeInBytes = datablock->SizeInBytes - currentOffset; - assert(meshBufferSizeInBytes > 0); + // TODO: Decode header + // TODO: Can we get rid of the malloc? - uint8_t* meshBufferData = (uint8_t*)malloc(sizeof(uint8_t) * meshBufferSizeInBytes); - fread(meshBufferData, sizeof(uint8_t), meshBufferSizeInBytes, file); + meshData->MeshPrimitives = (SampleMeshPrimitiveHeader*)malloc(sizeof(SampleMeshPrimitiveHeader) * meshData->MeshHeader.MeshPrimitiveCount); + fread(meshData->MeshPrimitives, sizeof(SampleMeshPrimitiveHeader), meshData->MeshHeader.MeshPrimitiveCount, file); - // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! - meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshBufferSizeInBytes, debugName); + fclose(file); +} + +// TODO: We should be able to replace this whole function with IO Queues +void SampleLoadMeshData(SampleMeshData* meshData, SampleGpuMemory* gpuMemory) +{ + assert(meshData->Path); + + FILE* file = SampleOpenFile(meshData->Path, true); + assert(file); + + fseek(file, meshData->MeshHeader.MeshBufferOffset, SEEK_SET); + + uint8_t* meshBufferData = (uint8_t*)malloc(sizeof(uint8_t) * meshData->MeshHeader.MeshBufferSizeInBytes); + fread(meshBufferData, sizeof(uint8_t), meshData->MeshHeader.MeshBufferSizeInBytes, file); + + // TODO: Construct debug name + meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshData->MeshHeader.MeshBufferSizeInBytes, "TO_CHANGE"); + + fclose(file); } void SampleFreeMesh(SampleMeshData* meshData) { - SampleFreeGpuBuffer(&meshData->MeshBuffer); + if(meshData->MeshBuffer.Buffer != ELEM_HANDLE_NULL) + { + SampleFreeGpuBuffer(&meshData->MeshBuffer); + } // TODO: Free mallocs } -void SampleLoadScene(const char* path, SampleGpuMemory* gpuMemory, SampleSceneData* sceneData) +void SampleLoadScene(const char* path, SampleSceneData* sceneData) { // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! @@ -72,12 +97,13 @@ void SampleLoadScene(const char* path, SampleGpuMemory* gpuMemory, SampleSceneDa SampleDataBlockEntry* meshDataBlocks = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * sceneHeader.MeshCount); fread(meshDataBlocks, sizeof(SampleDataBlockEntry), sceneHeader.MeshCount, file); + fclose(file); + for (uint32_t i = 0; i < sceneData->MeshCount; i++) { - SampleLoadMesh(file, &meshDataBlocks[i], gpuMemory, &sceneData->Meshes[i], "Mesh"); + SampleLoadMesh(path, meshDataBlocks[i].Offset, &sceneData->Meshes[i]); + //SampleLoadMeshData(file, gpuMemory, &sceneData->Meshes[i], "Mesh"); } - - fclose(file); } void SampleFreeScene(const SampleSceneData* sceneData) diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 62cb45a3..53e71c2e 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -2,12 +2,10 @@ struct ShaderParameters { uint32_t FrameDataBufferIndex; uint32_t MeshBuffer; - uint32_t MeshletCount; // TODO: Not used uint32_t VertexBufferOffset; uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; - uint32_t ShowMeshlets; }; [[vk::push_constant]] @@ -16,6 +14,7 @@ ShaderParameters parameters : register(b0); struct FrameData { float4x4 ViewProjMatrix; + uint32_t ShowMeshlets; }; struct ElemMeshlet @@ -39,6 +38,7 @@ struct VertexOutput float3 WorldNormal: NORMAL0; uint MeshletIndex : COLOR0; }; + #define IDENTITY_MATRIX float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) // TODO: Put that in an include file float4x4 TransformMatrix(float4 quaternion, float3 translation) @@ -114,7 +114,11 @@ uint hash(uint a) [shader("pixel")] float4 PixelMain(const VertexOutput input) : SV_Target0 { - if (parameters.ShowMeshlets == 0) + // TODO: Get the framedata only in the mesh shader? + ByteAddressBuffer frameDataBuffer = ResourceDescriptorHeap[parameters.FrameDataBufferIndex]; + FrameData frameData = frameDataBuffer.Load(0); + + if (frameData.ShowMeshlets == 0) { return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); } diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 8abfeba1..15de9a97 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -13,17 +13,16 @@ typedef struct uint32_t FrameDataBuffer; // TODO: Embed that into a buffer uint32_t MeshBuffer; - uint32_t MeshletCount; uint32_t VertexBufferOffset; uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; - uint32_t ShowMeshlets; } ShaderParameters; typedef struct { SampleMatrix4x4 ViewProjMatrix; + uint32_t ShowMeshlets; } ShaderFrameData; // TODO: Group common variables into separate structs @@ -76,9 +75,10 @@ void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, u // TODO: Copy paste the loading scene code from the common header because it is the first sample to explain how to load a mesh -void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 viewProjMatrix) +void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 viewProjMatrix, bool showMeshlets) { applicationPayload->FrameData.ViewProjMatrix = viewProjMatrix; + applicationPayload->FrameData.ShowMeshlets = showMeshlets; ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->FrameDataBuffer.Buffer); memcpy(vertexBufferPointer.Items, &applicationPayload->FrameData, sizeof(ShaderFrameData)); @@ -108,7 +108,7 @@ void InitSample(void* payload) applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBuffer.ReadDescriptor; CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - SampleLoadScene("sponza.scene", &applicationPayload->GpuMemory, &applicationPayload->TestSceneData); + SampleLoadScene("sponza.scene", &applicationPayload->TestSceneData); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -206,9 +206,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void SampleInputsCameraState* inputsCameraState = &applicationPayload->InputsCamera.State; - applicationPayload->ShaderParameters.ShowMeshlets = inputsCameraState->Action; - - UpdateFrameData(applicationPayload, inputsCameraState->ViewProjMatrix); + UpdateFrameData(applicationPayload, inputsCameraState->ViewProjMatrix, inputsCameraState->Action); ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); @@ -230,16 +228,26 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); - // TODO: Replace that all here is really bad + // TODO: Construct a list of tasks on the cpu for now and do only one dispatch mesh with the total of tasks + // Be carreful with the limit per dimension of 65000 for (uint32_t i = 0; i < applicationPayload->TestSceneData.MeshCount; i++) { SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[i]; + + if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) + { + // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data + // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check + // if the data was loaded. + // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. + SampleLoadMeshData(meshData, &applicationPayload->GpuMemory); + } + applicationPayload->ShaderParameters.MeshBuffer = meshData->MeshBuffer.ReadDescriptor; - for (uint32_t j = 0; j < meshData->MeshPartCount; j++) + for (uint32_t j = 0; j < meshData->MeshHeader.MeshPrimitiveCount; j++) { - SampleMeshPartHeader* meshPart = &meshData->MeshParts[j]; - applicationPayload->ShaderParameters.MeshletCount = meshPart->MeshletCount; + SampleMeshPrimitiveHeader* meshPart = &meshData->MeshPrimitives[j]; applicationPayload->ShaderParameters.VertexBufferOffset = meshPart->VertexBufferOffset; applicationPayload->ShaderParameters.MeshletOffset = meshPart->MeshletOffset; applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPart->MeshletVertexIndexOffset; @@ -274,8 +282,6 @@ int main(int argc, const char* argv[]) .AppSettings = SampleParseAppSettings(argc, argv) }; - // TODO: Load and save state - ElemConfigureLogHandler(ElemConsoleLogHandler); ElemRunApplication(&(ElemRunApplicationParameters) diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 0204a05a..8d0ddb44 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -7,26 +7,27 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) { assert(file); + uint32_t meshHeaderOffset = ftell(file); SampleMeshHeader meshHeader = { - .MeshPartCount = mesh.MeshParts.Length + .MeshPrimitiveCount = mesh.MeshPrimitives.Length }; fwrite(&meshHeader, sizeof(SampleMeshHeader), 1, file); - uint32_t meshPartHeadersOffset = ftell(file); + uint32_t meshPrimitiveHeadersOffset = ftell(file); - SampleMeshPartHeader* meshPartHeaders = (SampleMeshPartHeader*)malloc(sizeof(SampleMeshPartHeader) * mesh.MeshParts.Length); - fwrite(meshPartHeaders, sizeof(SampleMeshPartHeader), mesh.MeshParts.Length, file); + SampleMeshPrimitiveHeader* meshPrimitiveHeaders = (SampleMeshPrimitiveHeader*)malloc(sizeof(SampleMeshPrimitiveHeader) * mesh.MeshPrimitives.Length); + fwrite(meshPrimitiveHeaders, sizeof(SampleMeshPrimitiveHeader), mesh.MeshPrimitives.Length, file); - uint32_t meshPartBufferStartOffset = ftell(file); + meshHeader.MeshBufferOffset = ftell(file); - for (uint32_t i = 0; i < mesh.MeshParts.Length; i++) + for (uint32_t i = 0; i < mesh.MeshPrimitives.Length; i++) { - ElemSceneMeshPart* meshPart = &mesh.MeshParts.Items[i]; + ElemSceneMeshPrimitive* meshPrimitive = &mesh.MeshPrimitives.Items[i]; // TODO: Index buffer - ElemBuildMeshletResult result = ElemBuildMeshlets(meshPart->VertexBuffer, NULL); + ElemBuildMeshletResult result = ElemBuildMeshlets(meshPrimitive->VertexBuffer, NULL); DisplayOutputMessages("BuildMeshlets", result.Messages); @@ -35,24 +36,29 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) return false; } - SampleMeshPartHeader* meshPartHeader = &meshPartHeaders[i]; - meshPartHeader->MeshletCount = result.Meshlets.Length; + SampleMeshPrimitiveHeader* meshPrimitiveHeader = &meshPrimitiveHeaders[i]; + meshPrimitiveHeader->MeshletCount = result.Meshlets.Length; - meshPartHeader->VertexBufferOffset = ftell(file) - meshPartBufferStartOffset; + meshPrimitiveHeader->VertexBufferOffset = ftell(file) - meshHeader.MeshBufferOffset; fwrite(result.VertexBuffer.Data.Items, sizeof(uint8_t), result.VertexBuffer.Data.Length, file); - meshPartHeader->MeshletOffset = ftell(file) - meshPartBufferStartOffset; + meshPrimitiveHeader->MeshletOffset = ftell(file) - meshHeader.MeshBufferOffset; fwrite(result.Meshlets.Items, sizeof(ElemMeshlet), result.Meshlets.Length, file); - meshPartHeader->MeshletVertexIndexOffset = ftell(file) - meshPartBufferStartOffset; + meshPrimitiveHeader->MeshletVertexIndexOffset = ftell(file) - meshHeader.MeshBufferOffset; fwrite(result.MeshletVertexIndexBuffer.Items, sizeof(uint32_t), result.MeshletVertexIndexBuffer.Length, file); - meshPartHeader->MeshletTriangleIndexOffset = ftell(file) - meshPartBufferStartOffset; + meshPrimitiveHeader->MeshletTriangleIndexOffset = ftell(file) - meshHeader.MeshBufferOffset; fwrite(result.MeshletTriangleIndexBuffer.Items, sizeof(uint32_t), result.MeshletTriangleIndexBuffer.Length, file); } + + meshHeader.MeshBufferSizeInBytes = ftell(file) - meshHeader.MeshBufferOffset; + + fseek(file, meshHeaderOffset, SEEK_SET); + fwrite(&meshHeader, sizeof(SampleMeshHeader), 1, file); - fseek(file, meshPartHeadersOffset, SEEK_SET); - fwrite(meshPartHeaders, sizeof(SampleMeshPartHeader), mesh.MeshParts.Length, file); + fseek(file, meshPrimitiveHeadersOffset, SEEK_SET); + fwrite(meshPrimitiveHeaders, sizeof(SampleMeshPrimitiveHeader), mesh.MeshPrimitives.Length, file); fseek(file, 0, SEEK_END); @@ -72,9 +78,6 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) fwrite(&sceneHeader, sizeof(SampleSceneHeader), 1, file); // TODO: Get rid of malloc? - - // TODO: change the uint32_t to a struct of offset + size - SampleDataBlockEntry* meshDataOffsets = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * scene.Meshes.Length); fwrite(meshDataOffsets, sizeof(SampleDataBlockEntry), scene.Meshes.Length, file); @@ -88,7 +91,6 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) assert(result); meshDataOffsets[i] = (SampleDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; - printf("MeshDataOffset: %d, size=%d\n", meshDataOffsets[i].Offset, meshDataOffsets[i].SizeInBytes); } printf("Built meshlets in %.2fs\n", (SampleGetTimerValueInMS() - beforeMeshlets) / 1000.0); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 2430bd89..dafc9759 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -361,6 +361,7 @@ typedef enum ElemCommandQueueType_Compute = 1 } ElemCommandQueueType; +// TODO: Not needed? typedef enum { ElemIOCommandType_File = 0, @@ -636,6 +637,7 @@ typedef struct ElemFenceSpan FencesToWait; } ElemExecuteCommandListOptions; +// TODO: Not needed? typedef struct { ElemIOCommandType IOCommandType; @@ -707,6 +709,7 @@ typedef struct } ElemGraphicsResourceInfoOptions; // TODO: Mip Levels +// TODO: Clear values typedef struct { ElemGraphicsResourceType Type; diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 1af0b5c6..96e2f63b 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -250,17 +250,17 @@ typedef struct ElemVertexBuffer VertexBuffer; ElemUInt32Span IndexBuffer; // TODO: Material -} ElemSceneMeshPart; +} ElemSceneMeshPrimitive; typedef struct { - ElemSceneMeshPart* Items; + ElemSceneMeshPrimitive* Items; uint32_t Length; -} ElemSceneMeshPartSpan; +} ElemSceneMeshPrimitiveSpan; typedef struct { - ElemSceneMeshPartSpan MeshParts; + ElemSceneMeshPrimitiveSpan MeshPrimitives; // TODO: Bounding volumes } ElemSceneMesh; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index bcccc54c..d4b7017b 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -10,7 +10,7 @@ struct ObjLoaderFileData uint32_t CurrentOffset; }; -struct ObjMeshPartInfo +struct ObjMeshPrimitiveInfo { uint32_t IndexOffset; uint32_t IndexCount; @@ -111,7 +111,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPartInfo* meshPartInfo, const ElemLoadSceneOptions* options) +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPrimitiveInfo* meshPartInfo, const ElemLoadSceneOptions* options) { SystemAssert(options); auto coordinateSystem = options->CoordinateSystem; @@ -169,7 +169,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); - auto meshPartInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); + auto meshPartInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); for (uint32_t i = 0; i < objFileData->object_count; i++) { @@ -195,7 +195,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o } auto currentMaterial = objFileData->face_materials[currentFaceStart]; - printf(" MeshPart (Material: %d)\n", currentMaterial); + printf(" MeshPrimitive (Material: %d)\n", currentMaterial); auto meshPartCount = 0u; @@ -220,7 +220,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o { currentMaterial = faceMaterial; - printf(" MeshPart (Material: %d)\n", currentMaterial); + printf(" MeshPrimitive (Material: %d)\n", currentMaterial); auto meshPartIndexOffset = indexOffset + meshPartInfo->IndexCount; @@ -231,7 +231,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o meshPartInfo->IndexCount += faceVertexCount; } - auto meshParts = SystemPushArray(sceneLoaderMemoryArena, meshPartCount); + auto meshParts = SystemPushArray(sceneLoaderMemoryArena, meshPartCount); for (uint32_t i = 0; i < meshPartCount; i++) { @@ -241,7 +241,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o meshPart->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, objFileData, meshPartInfo, options); } - mesh->MeshParts = { .Items = meshParts.Pointer, .Length = (uint32_t)meshParts.Length }; + mesh->MeshPrimitives = { .Items = meshParts.Pointer, .Length = (uint32_t)meshParts.Length }; } return diff --git a/tests/ToolsTests/SceneLoaderTests.cpp b/tests/ToolsTests/SceneLoaderTests.cpp index 783814b8..406620c5 100644 --- a/tests/ToolsTests/SceneLoaderTests.cpp +++ b/tests/ToolsTests/SceneLoaderTests.cpp @@ -73,9 +73,9 @@ UTEST_F_TEARDOWN(SceneLoader_LoadScene) ASSERT_EQ_MSG(result.Meshes.Length, utest_fixture->ExpectedMeshCount, "Mesh count is not correct."); ElemSceneMesh mesh = result.Meshes.Items[0]; - ElemSceneMeshPart meshPart = mesh.MeshParts.Items[0]; + ElemSceneMeshPrimitive meshPart = mesh.MeshPrimitives.Items[0]; - ASSERT_EQ_MSG(meshPart.VertexBuffer.VertexCount, utest_fixture->ExpectedVertexCount, "MeshPart vertex count is not correct."); + ASSERT_EQ_MSG(meshPart.VertexBuffer.VertexCount, utest_fixture->ExpectedVertexCount, "MeshPrimitive vertex count is not correct."); ASSERT_GT_MSG(meshPart.VertexBuffer.VertexSize, 0u, "Vertex size must be greater than 0."); ASSERT_GT_MSG(meshPart.VertexBuffer.Data.Length, 0u, "VertexBuffer size must be greater than 0."); From 8c52eff8964c5f8e85bbb7a144f937e80088a80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 26 Dec 2024 15:12:12 +0100 Subject: [PATCH 43/93] Add scene nodes --- samples/Common/SampleInputsCamera.h | 42 +++-- samples/Common/SampleInputsModelViewer.h | 10 +- samples/Common/SampleMath.h | 82 ++++----- samples/Common/SampleScene.h | 18 ++ samples/Common/SampleSceneLoader.h | 11 +- samples/Elemental/03-HelloInputs/main.c | 16 +- samples/Elemental/99-Renderer/CMakeLists.txt | 2 + .../99-Renderer/Data/RenderMesh.hlsl | 8 +- samples/Elemental/99-Renderer/main.c | 53 +++--- .../ElementalTools/02-SceneCompiler/main.c | 22 ++- src/Elemental/Elemental.h | 10 ++ src/ElementalTools/ElementalTools.h | 27 ++- .../SceneLoading/SceneLoader.cpp | 5 + .../SceneLoading/SceneLoaderObj.cpp | 157 ++++++++++++++---- 14 files changed, 327 insertions(+), 136 deletions(-) diff --git a/samples/Common/SampleInputsCamera.h b/samples/Common/SampleInputsCamera.h index 7bb2a108..295b4fc9 100644 --- a/samples/Common/SampleInputsCamera.h +++ b/samples/Common/SampleInputsCamera.h @@ -29,10 +29,10 @@ typedef struct typedef struct { - SampleVector3 Position; - SampleVector3 PositionVelocity; - SampleVector3 Rotation; - SampleVector3 RotationVelocity; + ElemVector3 Position; + ElemVector3 PositionVelocity; + ElemVector3 Rotation; + ElemVector3 RotationVelocity; } SampleCamera; typedef struct @@ -88,17 +88,17 @@ void SampleInputsCameraInit(SampleInputsCamera* inputs) // TODO: Temporary Init code for now SampleCamera* camera = &(inputs->State.Camera); - camera->Position = (SampleVector3) { 0.0f, 100.0f, -10.0f }; - camera->Rotation = (SampleVector3) { 0.0f, 1.2f, 0 }; + camera->Position = (ElemVector3) { 0.0f, 2.0f, -1.0f }; + camera->Rotation = (ElemVector3) { 0.0f, 1.2f, 0 }; // TODO: Allow the reset of camera to the passed initial position when we press a button } -SampleVector3 ComputeDeltaAndVelocity(SampleVector3* currentVelocity, SampleVector3 directionVector, float accelerationFactor, float frictionFactor, float deltaTimeInSeconds) +ElemVector3 ComputeDeltaAndVelocity(ElemVector3* currentVelocity, ElemVector3 directionVector, float accelerationFactor, float frictionFactor, float deltaTimeInSeconds) { - SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(directionVector, accelerationFactor), SampleMulScalarV3(SampleInverseV3(*currentVelocity), frictionFactor)); + ElemVector3 acceleration = SampleAddV3(SampleMulScalarV3(directionVector, accelerationFactor), SampleMulScalarV3(SampleInverseV3(*currentVelocity), frictionFactor)); - SampleVector3 delta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(*currentVelocity, deltaTimeInSeconds)); + ElemVector3 delta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(*currentVelocity, deltaTimeInSeconds)); *currentVelocity = SampleAddV3(SampleMulScalarV3(acceleration, deltaTimeInSeconds), *currentVelocity); return delta; @@ -106,19 +106,17 @@ SampleVector3 ComputeDeltaAndVelocity(SampleVector3* currentVelocity, SampleVect SampleMatrix4x4 UpdateCamera(SampleCamera* camera, const SampleInputsCameraActions* inputActions, const ElemSwapChainUpdateParameters* updateParameters) { - // TODO: Should we normalize the input direction? It feels snappier without it. - - SampleVector3 movementDirection = (SampleVector3) + ElemVector3 movementDirection = (ElemVector3) { .X = (inputActions->MoveRight - inputActions->MoveLeft), .Y = 0.0f, .Z = (inputActions->MoveForward - inputActions->MoveBackward) }; - SampleVector3 movementDelta = ComputeDeltaAndVelocity(&camera->PositionVelocity, movementDirection, 15000.0f, 40.0f, updateParameters->DeltaTimeInSeconds); + ElemVector3 movementDelta = ComputeDeltaAndVelocity(&camera->PositionVelocity, movementDirection, 150.0f, 40.0f, updateParameters->DeltaTimeInSeconds); //printf("Current Pos Vel: %f %f %f\n", camera->PositionVelocity.X, camera->PositionVelocity.Y, camera->PositionVelocity.Z); - SampleVector3 rotationDirection = (SampleVector3) + ElemVector3 rotationDirection = (ElemVector3) { .X = (inputActions->RotateUp - inputActions->RotateDown), .Y = (inputActions->RotateLeft - inputActions->RotateRight), @@ -126,7 +124,7 @@ SampleMatrix4x4 UpdateCamera(SampleCamera* camera, const SampleInputsCameraActio }; // TODO: Prevent X Rotate above 180 deg - SampleVector3 rotationDelta = ComputeDeltaAndVelocity(&camera->RotationVelocity, rotationDirection, 80.0f, 40.0f, updateParameters->DeltaTimeInSeconds); + ElemVector3 rotationDelta = ComputeDeltaAndVelocity(&camera->RotationVelocity, rotationDirection, 80.0f, 40.0f, updateParameters->DeltaTimeInSeconds); if (SampleMagnitudeSquaredV3(rotationDelta)) { @@ -135,7 +133,7 @@ SampleMatrix4x4 UpdateCamera(SampleCamera* camera, const SampleInputsCameraActio if (inputActions->RotateMouse) { - SampleVector3 rotationMouseDelta = (SampleVector3) + ElemVector3 rotationMouseDelta = (ElemVector3) { .X = (inputActions->RotateMouseUp - inputActions->RotateMouseDown), .Y = (inputActions->RotateMouseLeft - inputActions->RotateMouseRight), @@ -149,22 +147,22 @@ SampleMatrix4x4 UpdateCamera(SampleCamera* camera, const SampleInputsCameraActio } // TODO: Review this - SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, camera->Rotation.X), - SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, camera->Rotation.Z), - SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, camera->Rotation.Y))); + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 1, 0, 0 }, camera->Rotation.X), + SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 0, 0, 1 }, camera->Rotation.Z), + SampleCreateQuaternion((ElemVector3){ 0, 1, 0 }, camera->Rotation.Y))); if (SampleMagnitudeSquaredV3(movementDelta)) { SampleMatrix4x4 translationTransform = SampleCreateTransformMatrix(rotationQuaternion, V3Zero); - SampleVector3 rotatedMovementDelta = SampleTransformPointV3(movementDelta, translationTransform); + ElemVector3 rotatedMovementDelta = SampleTransformPointV3(movementDelta, translationTransform); camera->Position = SampleAddV3(camera->Position, rotatedMovementDelta); } SampleMatrix4x4 targetTransform = SampleCreateTransformMatrix(rotationQuaternion, V3Zero); - SampleVector3 cameraTarget = SampleTransformPointV3((SampleVector3) { 0.0f, 0.0f, 1.0f }, targetTransform); + ElemVector3 cameraTarget = SampleTransformPointV3((ElemVector3) { 0.0f, 0.0f, 1.0f }, targetTransform); cameraTarget = SampleAddV3(cameraTarget, camera->Position); - return SampleCreateLookAtLHMatrix(camera->Position, cameraTarget, (SampleVector3) { 0.0f, 1.0f, 0.0f }); + return SampleCreateLookAtLHMatrix(camera->Position, cameraTarget, (ElemVector3) { 0.0f, 1.0f, 0.0f }); } void SampleInputsCameraUpdate(ElemInputStream inputStream, SampleInputsCamera* inputs, const ElemSwapChainUpdateParameters* updateParameters) diff --git a/samples/Common/SampleInputsModelViewer.h b/samples/Common/SampleInputsModelViewer.h index d215bfae..1f768dad 100644 --- a/samples/Common/SampleInputsModelViewer.h +++ b/samples/Common/SampleInputsModelViewer.h @@ -55,9 +55,9 @@ typedef struct typedef struct { - SampleVector3 RotationDelta; + ElemVector3 RotationDelta; SampleVector2 RotationTouch; - SampleVector3 CurrentRotationSpeed; + ElemVector3 CurrentRotationSpeed; float PreviousTouchDistance; float PreviousTouchAngle; float Zoom; @@ -222,7 +222,7 @@ void SampleInputsModelViewerUpdate(ElemInputStream inputStream, SampleInputsMode } else { - SampleVector3 direction = SampleNormalizeV3((SampleVector3) + ElemVector3 direction = SampleNormalizeV3((ElemVector3) { .X = (inputActions->RotateUp - inputActions->RotateDown), .Y = (inputActions->RotateLeft - inputActions->RotateRight), @@ -231,7 +231,7 @@ void SampleInputsModelViewerUpdate(ElemInputStream inputStream, SampleInputsMode if (SampleMagnitudeSquaredV3(direction)) { - SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, SAMPLE_MODELVIEWER_ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(state->CurrentRotationSpeed), SAMPLE_MODELVIEWER_ROTATION_FRICTION)); + ElemVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, SAMPLE_MODELVIEWER_ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(state->CurrentRotationSpeed), SAMPLE_MODELVIEWER_ROTATION_FRICTION)); SampleInputsModelViewerResetTouchParameters(state); state->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(state->CurrentRotationSpeed, deltaTimeInSeconds)); @@ -246,7 +246,7 @@ void SampleInputsModelViewerUpdate(ElemInputStream inputStream, SampleInputsMode state->RotationTouch = SampleMulScalarV2(SampleNormalizeV2(state->RotationTouch), SAMPLE_MODELVIEWER_ROTATION_TOUCH_MAX_DELTA); } - state->RotationDelta = SampleAddV3(state->RotationDelta, (SampleVector3){ state->RotationTouch.X, state->RotationTouch.Y, 0.0f }); + state->RotationDelta = SampleAddV3(state->RotationDelta, (ElemVector3){ state->RotationTouch.X, state->RotationTouch.Y, 0.0f }); SampleVector2 inverse = SampleMulScalarV2(SampleNormalizeV2(SampleInverseV2(state->RotationTouch)), SAMPLE_MODELVIEWER_ROTATION_TOUCH_DECREASE_SPEED); state->RotationTouch = SampleAddV2(state->RotationTouch, inverse); diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 8da3fe70..5b05fcab 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -1,8 +1,16 @@ #pragma once +#include + #define _USE_MATH_DEFINES #include +#ifdef ElemToolsAPI +typedef ElemToolsVector3 ElemVector3; +#else +#include "../Elemental/Elemental.h" +#endif + /** * WARNING: This math code is just for demonstration purpose only. All the math functions here focus * on readability and not performance. Don't use it in your production code! @@ -33,11 +41,6 @@ typedef struct float X, Y; } SampleVector2; -typedef struct -{ - float X, Y, Z; -} SampleVector3; - typedef union { struct @@ -47,7 +50,7 @@ typedef union struct { - SampleVector3 XYZ; + ElemVector3 XYZ; }; struct @@ -57,7 +60,7 @@ typedef union } SampleVector4; #define V2Zero (SampleVector2) { .X = 0.0f, .Y = 0.0f } -#define V3Zero (SampleVector3) { .X = 0.0f, .Y = 0.0f, .Z = 0.0f } +#define V3Zero (ElemVector3) { .X = 0.0f, .Y = 0.0f, .Z = 0.0f } SampleVector2 SampleInverseV2(SampleVector2 v) { @@ -137,9 +140,9 @@ SampleVector2 SampleNormalizeV2(SampleVector2 v) return v; } -SampleVector3 SampleInverseV3(SampleVector3 v) +ElemVector3 SampleInverseV3(ElemVector3 v) { - SampleVector3 result; + ElemVector3 result; result.X = -v.X; result.Y = -v.Y; @@ -148,9 +151,9 @@ SampleVector3 SampleInverseV3(SampleVector3 v) return result; } -SampleVector3 SampleAddV3(SampleVector3 v1, SampleVector3 v2) +ElemVector3 SampleAddV3(ElemVector3 v1, ElemVector3 v2) { - SampleVector3 result; + ElemVector3 result; result.X = v1.X + v2.X; result.Y = v1.Y + v2.Y; @@ -159,9 +162,9 @@ SampleVector3 SampleAddV3(SampleVector3 v1, SampleVector3 v2) return result; } -SampleVector3 SampleMinV3(SampleVector3 v1, SampleVector3 v2) +ElemVector3 SampleMinV3(ElemVector3 v1, ElemVector3 v2) { - SampleVector3 result; + ElemVector3 result; result.X = v1.X - v2.X; result.Y = v1.Y - v2.Y; @@ -170,9 +173,9 @@ SampleVector3 SampleMinV3(SampleVector3 v1, SampleVector3 v2) return result; } -SampleVector3 SampleMulScalarV3(SampleVector3 v, float scalar) +ElemVector3 SampleMulScalarV3(ElemVector3 v, float scalar) { - SampleVector3 result; + ElemVector3 result; result.X = v.X * scalar; result.Y = v.Y * scalar; @@ -181,9 +184,9 @@ SampleVector3 SampleMulScalarV3(SampleVector3 v, float scalar) return result; } -SampleVector3 SampleDivideScalarV3(SampleVector3 v, float scalar) +ElemVector3 SampleDivideScalarV3(ElemVector3 v, float scalar) { - SampleVector3 result; + ElemVector3 result; result.X = v.X / scalar; result.Y = v.Y / scalar; @@ -192,9 +195,9 @@ SampleVector3 SampleDivideScalarV3(SampleVector3 v, float scalar) return result; } -SampleVector3 SampleMulV3(SampleVector3 v1, SampleVector3 v2) +ElemVector3 SampleMulV3(ElemVector3 v1, ElemVector3 v2) { - SampleVector3 result; + ElemVector3 result; result.X = v1.X * v2.X; result.Y = v1.Y * v2.Y; @@ -203,44 +206,44 @@ SampleVector3 SampleMulV3(SampleVector3 v1, SampleVector3 v2) return result; } -float SampleDotProductV3(SampleVector3 v1, SampleVector3 v2) +float SampleDotProductV3(ElemVector3 v1, ElemVector3 v2) { return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z; } -float SampleMagnitudeSquaredV3(SampleVector3 v) +float SampleMagnitudeSquaredV3(ElemVector3 v) { return SampleDotProductV3(v, v); } -float SampleMagnitudeV3(SampleVector3 v) +float SampleMagnitudeV3(ElemVector3 v) { return sqrtf(SampleMagnitudeSquaredV3(v)); } -SampleVector3 SampleNormalizeV3(SampleVector3 v) +ElemVector3 SampleNormalizeV3(ElemVector3 v) { float magnitude = SampleMagnitudeV3(v); if (magnitude > 0.0f) { - SampleVector3 result = SampleMulScalarV3(v, (1.0f / SampleMagnitudeV3(v))); + ElemVector3 result = SampleMulScalarV3(v, (1.0f / SampleMagnitudeV3(v))); return result; } return v; } -SampleVector3 SampleCrossProductV3(SampleVector3 v1, SampleVector3 v2) +ElemVector3 SampleCrossProductV3(ElemVector3 v1, ElemVector3 v2) { - SampleVector3 result; + ElemVector3 result; result.X = v1.Y * v2.Z - v1.Z * v2.Y; result.Y = v1.Z * v2.X - v1.X * v2.Z; result.Z = v1.X * v2.Y - v1.Y * v2.X; return result; } -SampleVector4 SampleCreateQuaternion(SampleVector3 v, float w) +SampleVector4 SampleCreateQuaternion(ElemVector3 v, float w) { SampleVector4 result; @@ -301,7 +304,7 @@ SampleMatrix4x4 SampleCreateIdentityMatrix() return result; } -SampleMatrix4x4 SampleCreateTransformMatrix(SampleVector4 quaternion, SampleVector3 translation) +SampleMatrix4x4 SampleCreateTransformMatrix(SampleVector4 quaternion, ElemVector3 translation) { float xw = quaternion.X *quaternion.W , xx = quaternion.X *quaternion.X , yy = quaternion.Y *quaternion.Y , yw = quaternion.Y *quaternion.W , xy = quaternion.X *quaternion.Y , yz = quaternion.Y *quaternion.Z , @@ -409,11 +412,11 @@ SampleMatrix4x4 SampleCreateTranslationMatrix(float tx, float ty) return result; } -SampleMatrix4x4 SampleCreateLookAtLHMatrix(SampleVector3 eyePosition, SampleVector3 targetPosition, SampleVector3 upDirection) +SampleMatrix4x4 SampleCreateLookAtLHMatrix(ElemVector3 eyePosition, ElemVector3 targetPosition, ElemVector3 upDirection) { - SampleVector3 forwardDirection = SampleNormalizeV3(SampleMinV3(targetPosition, eyePosition)); - SampleVector3 rightDirection = SampleNormalizeV3(SampleCrossProductV3(upDirection, forwardDirection)); - SampleVector3 upDirectionNew = SampleCrossProductV3(forwardDirection, rightDirection); + ElemVector3 forwardDirection = SampleNormalizeV3(SampleMinV3(targetPosition, eyePosition)); + ElemVector3 rightDirection = SampleNormalizeV3(SampleCrossProductV3(upDirection, forwardDirection)); + ElemVector3 upDirectionNew = SampleCrossProductV3(forwardDirection, rightDirection); SampleMatrix4x4 result; @@ -443,9 +446,9 @@ SampleMatrix4x4 SampleMulMatrix4x4(SampleMatrix4x4 a, SampleMatrix4x4 b) { SampleMatrix4x4 result; - for (int i = 0; i < 4; i++) + for (uint32_t i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) + for (uint32_t j = 0; j < 4; j++) { result.m[i][j] = a.m[i][0] * b.m[0][j] + a.m[i][1] * b.m[1][j] + a.m[i][2] * b.m[2][j] + a.m[i][3] * b.m[3][j]; } @@ -454,7 +457,7 @@ SampleMatrix4x4 SampleMulMatrix4x4(SampleMatrix4x4 a, SampleMatrix4x4 b) return result; } -SampleVector2 SampleTransformPoint(SampleVector2 point, SampleMatrix4x4 m) +SampleVector2 SampleTransformPointV2(SampleVector2 point, SampleMatrix4x4 m) { SampleVector2 result; @@ -464,17 +467,14 @@ SampleVector2 SampleTransformPoint(SampleVector2 point, SampleMatrix4x4 m) return result; } -SampleVector3 SampleTransformPointV3(SampleVector3 point, SampleMatrix4x4 m) +ElemVector3 SampleTransformPointV3(ElemVector3 point, SampleMatrix4x4 m) { - SampleVector3 result; + ElemVector3 result; result.X = m.m[0][0] * point.X + m.m[0][1] * point.Y + m.m[0][2] * point.Z + m.m[0][3]; result.Y = m.m[1][0] * point.X + m.m[1][1] * point.Y + m.m[1][2] * point.Z + m.m[1][3]; result.Z = m.m[2][0] * point.X + m.m[2][1] * point.Y + m.m[2][2] * point.Z + m.m[2][3]; - //result.X = m.m[0][0] * point.X + m.m[1][0] * point.Y + m.m[2][0] * point.Z + m.m[3][0]; - //result.Y = m.m[0][1] * point.X + m.m[1][1] * point.Y + m.m[2][1] * point.Z + m.m[3][1]; - //result.Z = m.m[0][2] * point.X + m.m[1][2] * point.Y + m.m[2][2] * point.Z + m.m[3][2]; - return result; } + diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h index 372b1309..93dc2873 100644 --- a/samples/Common/SampleScene.h +++ b/samples/Common/SampleScene.h @@ -1,13 +1,31 @@ #pragma once +#include "SampleMath.h" #include +typedef enum +{ + SampleSceneNodeType_Unknown = 0, + SampleSceneNodeType_Mesh = 1 +} SampleSceneNodeType; + typedef struct { char FileId[5]; uint32_t MeshCount; + uint32_t NodeCount; } SampleSceneHeader; +typedef struct +{ + char Name[50]; + SampleSceneNodeType NodeType; + int32_t ReferenceIndex; + ElemVector3 Rotation; + ElemVector3 Translation; + // TODO: Children +} SampleSceneNodeHeader; + typedef struct { // TODO: Vertex size, etc. diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 0ce02207..d7d5758d 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -5,6 +5,7 @@ #include "SampleScene.h" #include "SampleGpuMemory.h" +// TODO: The struct here are temporary. The final data will be placed in buffer typedef struct { const char* Path; @@ -17,6 +18,8 @@ typedef struct { uint32_t MeshCount; SampleMeshData* Meshes; + uint32_t NodeCount; + SampleSceneNodeHeader* Nodes; } SampleSceneData; void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) @@ -87,9 +90,10 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) printf("ERROR: Wrong scene format\n"); } - printf("OK Meshes Count: %d\n", sceneHeader.MeshCount); + printf("Scene Loaded: Meshes Count=%d, Nodes Count=%d\n", sceneHeader.MeshCount, sceneHeader.NodeCount); sceneData->MeshCount = sceneHeader.MeshCount; + sceneData->NodeCount = sceneHeader.NodeCount; // TODO: Replace malloc with an utility function that we will replace sceneData->Meshes = (SampleMeshData*)malloc(sizeof(SampleMeshData) * sceneHeader.MeshCount); @@ -97,6 +101,9 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) SampleDataBlockEntry* meshDataBlocks = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * sceneHeader.MeshCount); fread(meshDataBlocks, sizeof(SampleDataBlockEntry), sceneHeader.MeshCount, file); + sceneData->Nodes = (SampleSceneNodeHeader*)malloc(sizeof(SampleSceneNodeHeader) * sceneHeader.NodeCount); + fread(sceneData->Nodes, sizeof(SampleSceneNodeHeader), sceneHeader.NodeCount, file); + fclose(file); for (uint32_t i = 0; i < sceneData->MeshCount; i++) @@ -104,6 +111,8 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) SampleLoadMesh(path, meshDataBlocks[i].Offset, &sceneData->Meshes[i]); //SampleLoadMeshData(file, gpuMemory, &sceneData->Meshes[i], "Mesh"); } + + free(meshDataBlocks); } void SampleFreeScene(const SampleSceneData* sceneData) diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/03-HelloInputs/main.c index 404a55ea..4386b02f 100644 --- a/samples/Elemental/03-HelloInputs/main.c +++ b/samples/Elemental/03-HelloInputs/main.c @@ -78,9 +78,9 @@ typedef struct typedef struct { - SampleVector3 RotationDelta; + ElemVector3 RotationDelta; SampleVector2 RotationTouch; - SampleVector3 CurrentRotationSpeed; + ElemVector3 CurrentRotationSpeed; float PreviousTouchDistance; float PreviousTouchAngle; float Zoom; @@ -324,7 +324,7 @@ void UpdateGameState(GameState* gameState, InputActions* inputActions, float del } else { - SampleVector3 direction = SampleNormalizeV3((SampleVector3) + ElemVector3 direction = SampleNormalizeV3((ElemVector3) { .X = (inputActions->RotateUp - inputActions->RotateDown), .Y = (inputActions->RotateLeft - inputActions->RotateRight), @@ -333,7 +333,7 @@ void UpdateGameState(GameState* gameState, InputActions* inputActions, float del if (SampleMagnitudeSquaredV3(direction)) { - SampleVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(gameState->CurrentRotationSpeed), ROTATION_FRICTION)); + ElemVector3 acceleration = SampleAddV3(SampleMulScalarV3(direction, ROTATION_ACCELERATION), SampleMulScalarV3(SampleInverseV3(gameState->CurrentRotationSpeed), ROTATION_FRICTION)); ResetTouchParameters(gameState); gameState->RotationDelta = SampleAddV3(SampleMulScalarV3(acceleration, 0.5f * SamplePow2f(deltaTimeInSeconds)), SampleMulScalarV3(gameState->CurrentRotationSpeed, deltaTimeInSeconds)); @@ -348,7 +348,7 @@ void UpdateGameState(GameState* gameState, InputActions* inputActions, float del gameState->RotationTouch = SampleMulScalarV2(SampleNormalizeV2(gameState->RotationTouch), ROTATION_TOUCH_MAX_DELTA); } - gameState->RotationDelta = SampleAddV3(gameState->RotationDelta, (SampleVector3){ gameState->RotationTouch.X, gameState->RotationTouch.Y, 0.0f }); + gameState->RotationDelta = SampleAddV3(gameState->RotationDelta, (ElemVector3){ gameState->RotationTouch.X, gameState->RotationTouch.Y, 0.0f }); SampleVector2 inverse = SampleMulScalarV2(SampleNormalizeV2(SampleInverseV2(gameState->RotationTouch)), ROTATION_TOUCH_DECREASE_SPEED); gameState->RotationTouch = SampleAddV2(gameState->RotationTouch, inverse); @@ -388,9 +388,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void if (SampleMagnitudeSquaredV3(gameState->RotationDelta)) { - SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, gameState->RotationDelta.X), - SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, gameState->RotationDelta.Z), - SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, gameState->RotationDelta.Y))); + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 1, 0, 0 }, gameState->RotationDelta.X), + SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 0, 0, 1 }, gameState->RotationDelta.Z), + SampleCreateQuaternion((ElemVector3){ 0, 1, 0 }, gameState->RotationDelta.Y))); applicationPayload->ShaderParameters.RotationQuaternion = SampleMulQuat(rotationQuaternion, applicationPayload->ShaderParameters.RotationQuaternion); } diff --git a/samples/Elemental/99-Renderer/CMakeLists.txt b/samples/Elemental/99-Renderer/CMakeLists.txt index 3ec9ef6f..aa9a059e 100644 --- a/samples/Elemental/99-Renderer/CMakeLists.txt +++ b/samples/Elemental/99-Renderer/CMakeLists.txt @@ -7,5 +7,7 @@ target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) add_dependencies(${SAMPLE_NAME} SampleSharedData) +# TODO: Allow passing parameters per data file + configure_resource_compilation(${SAMPLE_NAME} resource_list) configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 53e71c2e..3311535a 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -1,3 +1,5 @@ + +// TODO: Shader parameters here are temporary struct ShaderParameters { uint32_t FrameDataBufferIndex; @@ -6,6 +8,7 @@ struct ShaderParameters uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; + float3 Translation; }; [[vk::push_constant]] @@ -50,7 +53,7 @@ float4x4 TransformMatrix(float4 quaternion, float3 translation) float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); - float4 row4 = float4(0.0, 0.0, 0.0, 1.0); + float4 row4 = float4(translation.x, translation.y, translation.z, 1.0); return float4x4(row1, row2, row3, row4); } @@ -76,8 +79,7 @@ void MeshMain(in uint groupId: SV_GroupID, ByteAddressBuffer frameDataBuffer = ResourceDescriptorHeap[parameters.FrameDataBufferIndex]; FrameData frameData = frameDataBuffer.Load(0); - //float4x4 worldMatrix = TransformMatrix(parameters.RotationQuaternion, float3(0.0, 0.0, 0.0)); - float4x4 worldMatrix = IDENTITY_MATRIX; + float4x4 worldMatrix = TransformMatrix(float4(0, 0, 0, 1), parameters.Translation); float4x4 inverseTransposeWorldMatrix = worldMatrix; diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 15de9a97..5f977cf4 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -17,6 +17,9 @@ typedef struct uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; + uint32_t Reserved1; + uint32_t Reserved2; + ElemVector3 Translation; } ShaderParameters; typedef struct @@ -230,31 +233,37 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void // TODO: Construct a list of tasks on the cpu for now and do only one dispatch mesh with the total of tasks // Be carreful with the limit per dimension of 65000 - for (uint32_t i = 0; i < applicationPayload->TestSceneData.MeshCount; i++) + for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) { - SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[i]; - - if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) - { - // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data - // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check - // if the data was loaded. - // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. - SampleLoadMeshData(meshData, &applicationPayload->GpuMemory); - } - - applicationPayload->ShaderParameters.MeshBuffer = meshData->MeshBuffer.ReadDescriptor; + SampleSceneNodeHeader* sceneNode = &applicationPayload->TestSceneData.Nodes[i]; - for (uint32_t j = 0; j < meshData->MeshHeader.MeshPrimitiveCount; j++) + if (sceneNode->NodeType == SampleSceneNodeType_Mesh) { - SampleMeshPrimitiveHeader* meshPart = &meshData->MeshPrimitives[j]; - applicationPayload->ShaderParameters.VertexBufferOffset = meshPart->VertexBufferOffset; - applicationPayload->ShaderParameters.MeshletOffset = meshPart->MeshletOffset; - applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPart->MeshletVertexIndexOffset; - applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPart->MeshletTriangleIndexOffset; - - ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - ElemDispatchMesh(commandList, meshPart->MeshletCount, 1, 1); + SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[sceneNode->ReferenceIndex]; + + if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) + { + // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data + // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check + // if the data was loaded. + // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. + SampleLoadMeshData(meshData, &applicationPayload->GpuMemory); + } + + applicationPayload->ShaderParameters.MeshBuffer = meshData->MeshBuffer.ReadDescriptor; + + for (uint32_t j = 0; j < meshData->MeshHeader.MeshPrimitiveCount; j++) + { + SampleMeshPrimitiveHeader* meshPrimitive = &meshData->MeshPrimitives[j]; + applicationPayload->ShaderParameters.VertexBufferOffset = meshPrimitive->VertexBufferOffset; + applicationPayload->ShaderParameters.MeshletOffset = meshPrimitive->MeshletOffset; + applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPrimitive->MeshletVertexIndexOffset; + applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPrimitive->MeshletTriangleIndexOffset; + applicationPayload->ShaderParameters.Translation = sceneNode->Translation; + + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); + ElemDispatchMesh(commandList, meshPrimitive->MeshletCount, 1, 1); + } } } diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 8d0ddb44..0c6d6847 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -73,6 +73,7 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) { .FileId = { 'S', 'C', 'E', 'N', 'E' }, .MeshCount = scene.Meshes.Length, + .NodeCount = scene.Nodes.Length }; fwrite(&sceneHeader, sizeof(SampleSceneHeader), 1, file); @@ -81,6 +82,24 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) SampleDataBlockEntry* meshDataOffsets = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * scene.Meshes.Length); fwrite(meshDataOffsets, sizeof(SampleDataBlockEntry), scene.Meshes.Length, file); + for (uint32_t i = 0; i < scene.Nodes.Length; i++) + { + ElemSceneNode* node = &scene.Nodes.Items[i]; + + SampleSceneNodeHeader fileNode = + { + .NodeType = (SampleSceneNodeType)node->NodeType, + .ReferenceIndex = node->ReferenceIndex, + .Rotation = node->Rotation, + .Translation = node->Translation + }; + + strncpy(fileNode.Name, node->Name, 50); + fwrite(&fileNode, sizeof(SampleSceneNodeHeader), 1, file); + + printf("Node: '%s' MeshId=%d (T=%f,%f,%f)\n", node->Name, node->ReferenceIndex, node->Translation.X, node->Translation.Y, node->Translation.Z); + } + double beforeMeshlets = SampleGetTimerValueInMS(); for (uint32_t i = 0; i < scene.Meshes.Length; i++) @@ -164,7 +183,8 @@ int main(int argc, const char* argv[]) return 1; } - ElemLoadSceneResult scene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .CoordinateSystem = ElemSceneCoordinateSystem_LeftHanded }); + // TODO: Scaling should be passed as a parameter + ElemLoadSceneResult scene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .CoordinateSystem = ElemSceneCoordinateSystem_LeftHanded, .Scaling = 0.01f }); DisplayOutputMessages("LoadScene", scene.Messages); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index dafc9759..35118bd0 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -887,6 +887,16 @@ typedef struct uint32_t Length; } ElemViewportSpan; +typedef struct +{ + float X, Y, Z; +} ElemVector3; + +typedef struct +{ + ElemVector3 MinPoint; + ElemVector3 MaxPoint; +} ElemBoundingBox; /** * Configuration for a render pass target. */ diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 96e2f63b..8d2ccb6c 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -123,7 +123,13 @@ typedef struct typedef struct { float X, Y, Z; -} ElemVector3; +} ElemToolsVector3; + +typedef struct +{ + ElemToolsVector3 MinPoint; + ElemToolsVector3 MaxPoint; +} ElemToolsBoundingBox; typedef struct { @@ -239,9 +245,16 @@ typedef enum ElemSceneCoordinateSystem_RightHanded = 1 } ElemSceneCoordinateSystem; +typedef enum +{ + ElemSceneNodeType_Unknown = 0, + ElemSceneNodeType_Mesh = 1 +} ElemSceneNodeType; + typedef struct { ElemSceneCoordinateSystem CoordinateSystem; + float Scaling; // TODO: Add options to specify the vertex format wanted } ElemLoadSceneOptions; @@ -249,6 +262,8 @@ typedef struct { ElemVertexBuffer VertexBuffer; ElemUInt32Span IndexBuffer; + ElemToolsBoundingBox BoundingBox; + // TODO: SphereVolume? // TODO: Material } ElemSceneMeshPrimitive; @@ -261,7 +276,8 @@ typedef struct typedef struct { ElemSceneMeshPrimitiveSpan MeshPrimitives; - // TODO: Bounding volumes + ElemToolsBoundingBox BoundingBox; + // TODO: SphereVolume? } ElemSceneMesh; typedef struct @@ -273,9 +289,10 @@ typedef struct typedef struct { const char* Name; - int32_t MeshIndex; - ElemVector3 Rotation; - ElemVector3 Translation; + ElemSceneNodeType NodeType; + int32_t ReferenceIndex; + ElemToolsVector3 Rotation; + ElemToolsVector3 Translation; // TODO: Children } ElemSceneNode; diff --git a/src/ElementalTools/SceneLoading/SceneLoader.cpp b/src/ElementalTools/SceneLoading/SceneLoader.cpp index 8ef04b1c..4d2f6559 100644 --- a/src/ElementalTools/SceneLoading/SceneLoader.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoader.cpp @@ -50,6 +50,11 @@ ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadS loadSceneOptions = *options; } + if (loadSceneOptions.Scaling == 0.0f) + { + loadSceneOptions.Scaling = 1.0f; + } + auto sceneFormat = GetSceneFormatFromPath(path); // TODO: Refactor that with array entries and function pointer diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index d4b7017b..df143390 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -14,8 +14,47 @@ struct ObjMeshPrimitiveInfo { uint32_t IndexOffset; uint32_t IndexCount; + ElemToolsBoundingBox BoundingBox; }; +void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundingBox) +{ + float minX = (point.X < boundingBox->MinPoint.X) ? point.X : boundingBox->MinPoint.X; + float minY = (point.Y < boundingBox->MinPoint.Y) ? point.Y : boundingBox->MinPoint.Y; + float minZ = (point.Z < boundingBox->MinPoint.Z) ? point.Z : boundingBox->MinPoint.Z; + + float maxX = (point.X > boundingBox->MaxPoint.X) ? point.X : boundingBox->MaxPoint.X; + float maxY = (point.Y > boundingBox->MaxPoint.Y) ? point.Y : boundingBox->MaxPoint.Y; + float maxZ = (point.Z > boundingBox->MaxPoint.Z) ? point.Z : boundingBox->MaxPoint.Z; + + boundingBox->MinPoint = { minX, minY, minZ }; + boundingBox->MaxPoint = { maxX, maxY, maxZ }; +} + +void AddBoundingBoxToBoundingBox(const ElemToolsBoundingBox* additional, ElemToolsBoundingBox* boundingBox) +{ + float minX = SystemMin(boundingBox->MinPoint.X, additional->MinPoint.X); + float minY = SystemMin(boundingBox->MinPoint.Y, additional->MinPoint.Y); + float minZ = SystemMin(boundingBox->MinPoint.Z, additional->MinPoint.Z); + + float maxX = SystemMax(boundingBox->MaxPoint.X, additional->MaxPoint.X); + float maxY = SystemMax(boundingBox->MaxPoint.Y, additional->MaxPoint.Y); + float maxZ = SystemMax(boundingBox->MaxPoint.Z, additional->MaxPoint.Z); + + boundingBox->MinPoint = { minX, minY, minZ }; + boundingBox->MaxPoint = { maxX, maxY, maxZ }; +} + +ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox) +{ + return + { + .X = (boundingBox->MinPoint.X + boundingBox->MaxPoint.X) * 0.5f, + .Y = (boundingBox->MinPoint.Y + boundingBox->MaxPoint.Y) * 0.5f, + .Z = (boundingBox->MinPoint.Z + boundingBox->MaxPoint.Z) * 0.5f + }; +} + void* FastObjFileOpen(const char* path, void* userData) { auto objFileData = LoadFileData(path); @@ -63,7 +102,7 @@ unsigned long FastObjFileSize(void* file, void* userData) return objLoaderFileData->FileData.Length; } -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer) +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, float scaling, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) { // TODO: Check what to include auto currentVertexBufferPointer = *vertexBufferPointer; @@ -72,7 +111,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc { for (uint32_t j = 0; j < 3; j++) { - ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; + ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j] * scaling; } if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) @@ -80,6 +119,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; } + AddPointToBoundingBox({ ((float*)currentVertexBufferPointer)[0], ((float*)currentVertexBufferPointer)[1], ((float*)currentVertexBufferPointer)[2]}, boundingBox); currentVertexBufferPointer += 3 * sizeof(float); } @@ -111,10 +151,11 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPrimitiveInfo* meshPartInfo, const ElemLoadSceneOptions* options) +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo, const ElemLoadSceneOptions* options) { SystemAssert(options); auto coordinateSystem = options->CoordinateSystem; + auto scaling = options->Scaling; // TODO: Config of the vertex components to load auto positionSize = sizeof(float) * 3; @@ -125,22 +166,22 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj // TODO: Temporary auto realVertexSize = maxVertexSize; - auto vertexBuffer = SystemPushArray(memoryArena, meshPartInfo->IndexCount * maxVertexSize); + auto vertexBuffer = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount * maxVertexSize); auto currentVertexBufferPointer = vertexBuffer.Pointer; - for (uint32_t i = meshPartInfo->IndexOffset; i < meshPartInfo->IndexOffset + meshPartInfo->IndexCount; i += 3) + for (uint32_t i = meshPrimitiveInfo->IndexOffset; i < meshPrimitiveInfo->IndexOffset + meshPrimitiveInfo->IndexCount; i += 3) { if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); } else { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, ¤tVertexBufferPointer); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, ¤tVertexBufferPointer); + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); + ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); + ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); } } @@ -148,10 +189,25 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj { .Data = { .Items = vertexBuffer.Pointer, .Length = (uint32_t)vertexBuffer.Length }, .VertexSize = (uint32_t)realVertexSize, - .VertexCount = meshPartInfo->IndexCount + .VertexCount = meshPrimitiveInfo->IndexCount }; } +void ApplyObjMeshPrimitiveInverseTranslation(ElemToolsVector3 translation, ElemVertexBuffer* vertexBuffer) +{ + for (uint32_t i = 0; i < vertexBuffer->VertexCount; i++) + { + auto position = (ElemToolsVector3*)(vertexBuffer->Data.Items + i * vertexBuffer->VertexSize); + *position = { position->X - translation.X, position->Y - translation.Y, position->Z - translation.Z }; + } +} + +void ApplyObjBoundingBoxInverseTranslation(ElemToolsVector3 translation, ElemToolsBoundingBox* boundingBox) +{ + boundingBox->MinPoint = { boundingBox->MinPoint.X - translation.X, boundingBox->MinPoint.Y - translation.Y, boundingBox->MinPoint.Z - translation.Y }; + boundingBox->MaxPoint = { boundingBox->MaxPoint.X - translation.X, boundingBox->MaxPoint.Y - translation.Y, boundingBox->MaxPoint.Z - translation.Y }; +} + ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* options) { auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -169,11 +225,13 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); - auto meshPartInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); + auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); + auto meshPrimitiveInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); for (uint32_t i = 0; i < objFileData->object_count; i++) { auto mesh = &meshes[i]; + auto sceneNode = &sceneNodes[i]; auto objectData = &objFileData->objects[i]; if (objectData->face_count == 0) @@ -197,10 +255,18 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto currentMaterial = objFileData->face_materials[currentFaceStart]; printf(" MeshPrimitive (Material: %d)\n", currentMaterial); - auto meshPartCount = 0u; - - auto meshPartInfo = &meshPartInfos[meshPartCount++]; - *meshPartInfo = { .IndexOffset = indexOffset }; + auto meshPrimitiveCount = 0u; + + auto meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; + *meshPrimitiveInfo = + { + .IndexOffset = indexOffset, + .BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } + } + }; for (uint32_t j = currentFaceStart; j < currentFaceEnd; j++) { @@ -222,26 +288,60 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o printf(" MeshPrimitive (Material: %d)\n", currentMaterial); - auto meshPartIndexOffset = indexOffset + meshPartInfo->IndexCount; - - meshPartInfo = &meshPartInfos[meshPartCount++]; - *meshPartInfo = { .IndexOffset = meshPartIndexOffset }; + auto meshPrimitiveIndexOffset = indexOffset + meshPrimitiveInfo->IndexCount; + + meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; + *meshPrimitiveInfo = + { + .IndexOffset = meshPrimitiveIndexOffset, + .BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { FLT_MIN, FLT_MIN, FLT_MIN } + } + }; } - meshPartInfo->IndexCount += faceVertexCount; + meshPrimitiveInfo->IndexCount += faceVertexCount; } - auto meshParts = SystemPushArray(sceneLoaderMemoryArena, meshPartCount); + mesh->BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } + }; + + auto meshPrimitives = SystemPushArray(sceneLoaderMemoryArena, meshPrimitiveCount); + + for (uint32_t j = 0; j < meshPrimitiveCount; j++) + { + auto meshPrimitive = &meshPrimitives[j]; + auto meshPrimitiveInfo = &meshPrimitiveInfos[j]; + + meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, objFileData, meshPrimitiveInfo, options); + meshPrimitive->BoundingBox = meshPrimitiveInfo->BoundingBox; + + AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); + } + + auto boundingBoxCenter = GetBoundingBoxCenter(&mesh->BoundingBox); + ApplyObjBoundingBoxInverseTranslation(boundingBoxCenter, &mesh->BoundingBox); - for (uint32_t i = 0; i < meshPartCount; i++) + for (uint32_t j = 0; j < meshPrimitiveCount; j++) { - auto meshPart = &meshParts[i]; - auto meshPartInfo = &meshPartInfos[i]; + auto meshPrimitive = &meshPrimitives[j]; - meshPart->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, objFileData, meshPartInfo, options); + ApplyObjMeshPrimitiveInverseTranslation(boundingBoxCenter, &meshPrimitive->VertexBuffer); + ApplyObjBoundingBoxInverseTranslation(boundingBoxCenter, &meshPrimitive->BoundingBox); } - mesh->MeshPrimitives = { .Items = meshParts.Pointer, .Length = (uint32_t)meshParts.Length }; + mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; + + sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + sceneNode->NodeType = ElemSceneNodeType_Mesh; + sceneNode->ReferenceIndex = i; + sceneNode->Rotation = {}; + sceneNode->Translation = boundingBoxCenter; } return @@ -249,6 +349,7 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o .SceneFormat = ElemSceneFormat_Obj, .CoordinateSystem = options->CoordinateSystem, .Meshes = { .Items = meshes.Pointer, .Length = (uint32_t)meshes.Length }, + .Nodes = { .Items = sceneNodes.Pointer, .Length = (uint32_t)sceneNodes.Length }, .HasErrors = hasErrors }; } From e3b861c08796bb707276d33a21a2e13835897e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 27 Dec 2024 16:17:42 +0100 Subject: [PATCH 44/93] Add basic support to glTF scenes --- .gitignore | 2 + .gitmodules | 3 + external/CMakeLists.txt | 10 +- external/cgltf | 1 + samples/Common/SampleMath.h | 8 +- samples/Common/SampleScene.h | 4 +- samples/Common/SampleSceneLoader.h | 2 +- .../99-Renderer/Data/RenderMesh.hlsl | 30 +- samples/Elemental/99-Renderer/main.c | 22 +- .../ElementalTools/02-SceneCompiler/main.c | 23 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 1 + src/Elemental/Elemental.h | 13 + src/ElementalTools/ElementalTools.h | 32 +- src/ElementalTools/ElementalToolsLoader.c | 8 +- src/ElementalTools/Meshes/MeshletBuilder.cpp | 15 +- .../Platforms/Apple/PreCompiledHeader.h | 5 +- .../Platforms/Linux/PreCompiledHeader.h | 2 + .../Platforms/Microsoft/PreCompiledHeader.h | 1 + .../SceneLoading/SceneLoader.cpp | 26 +- src/ElementalTools/SceneLoading/SceneLoader.h | 2 + .../SceneLoading/SceneLoaderGltf.cpp | 275 ++++++++++++++++++ .../SceneLoading/SceneLoaderObj.cpp | 104 +++---- src/ElementalTools/ToolsUtils.cpp | 223 ++++++++++++++ src/ElementalTools/ToolsUtils.h | 15 + src/ElementalTools/UnityBuild.cpp | 3 + tests/ToolsTests/MeshBuilderTests.cpp | 29 +- 26 files changed, 749 insertions(+), 110 deletions(-) create mode 160000 external/cgltf create mode 100644 src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp diff --git a/.gitignore b/.gitignore index 7257fc06..f7b8552c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ *.obj *.mesh *.ini +*.gltf +*.bin xcuserdata/ packages/ diff --git a/.gitmodules b/.gitmodules index 547364f6..db47cd7e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "external/fast_obj"] path = external/fast_obj url = https://github.com/thisistherk/fast_obj.git +[submodule "external/cgltf"] + path = external/cgltf + url = https://github.com/jkuhlmann/cgltf.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 86a0b040..dfac4dc8 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -217,11 +217,19 @@ if(NOT BUILD_FOR_IOS) add_subdirectory(./fast_obj) endif() +#======================================================================= +# cgltf +#======================================================================= +if(NOT BUILD_FOR_IOS) + add_library(cgltf INTERFACE) + target_include_directories(cgltf INTERFACE ./cgltf/) +endif() + #======================================================================= # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf) #======================================================================= # xxHash diff --git a/external/cgltf b/external/cgltf new file mode 160000 index 00000000..7177c019 --- /dev/null +++ b/external/cgltf @@ -0,0 +1 @@ +Subproject commit 7177c019be6c105fd12bc35f249e431314f2bab1 diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 5b05fcab..3abea847 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -7,6 +7,7 @@ #ifdef ElemToolsAPI typedef ElemToolsVector3 ElemVector3; +typedef ElemToolsVector4 ElemVector4; #else #include "../Elemental/Elemental.h" #endif @@ -271,6 +272,7 @@ SampleVector4 SampleMulQuat(SampleVector4 q1, SampleVector4 q2) return (SampleVector4) { .X = x, .Y = y, .Z = z, .W = w }; } +// TODO: Get rid of this typedef union { float m[4][4]; @@ -304,6 +306,7 @@ SampleMatrix4x4 SampleCreateIdentityMatrix() return result; } +// TODO: To review SampleMatrix4x4 SampleCreateTransformMatrix(SampleVector4 quaternion, ElemVector3 translation) { float xw = quaternion.X *quaternion.W , xx = quaternion.X *quaternion.X , yy = quaternion.Y *quaternion.Y , @@ -349,6 +352,7 @@ SampleMatrix4x4 SampleTransposeMatrix(SampleMatrix4x4 matrix) SampleMatrix4x4 SampleCreateRotationMatrix(float angle) { + // BUG: Uninit values! SampleMatrix4x4 result; float c = cosf(angle); float s = sinf(angle); @@ -372,6 +376,7 @@ SampleMatrix4x4 SampleCreateRotationMatrix(float angle) SampleMatrix4x4 SampleCreateScaleMatrix(float scale) { + // BUG: Uninit values! SampleMatrix4x4 result; result.m[0][0] = scale; @@ -384,7 +389,7 @@ SampleMatrix4x4 SampleCreateScaleMatrix(float scale) result.m[2][0] = 0.0f; result.m[2][1] = 0.0f; - result.m[2][2] = 1.0f; + result.m[2][2] = scale; result.m[3][3] = 1.0f; @@ -393,6 +398,7 @@ SampleMatrix4x4 SampleCreateScaleMatrix(float scale) SampleMatrix4x4 SampleCreateTranslationMatrix(float tx, float ty) { + // BUG: Uninit values! SampleMatrix4x4 result; result.m[0][0] = 1.0f; diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h index 93dc2873..b80c859a 100644 --- a/samples/Common/SampleScene.h +++ b/samples/Common/SampleScene.h @@ -21,13 +21,15 @@ typedef struct char Name[50]; SampleSceneNodeType NodeType; int32_t ReferenceIndex; - ElemVector3 Rotation; + ElemVector4 Rotation; + float Scale; ElemVector3 Translation; // TODO: Children } SampleSceneNodeHeader; typedef struct { + char Name[50]; // TODO: Vertex size, etc. uint32_t MeshPrimitiveCount; uint32_t MeshBufferOffset; diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index d7d5758d..8761182f 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -60,7 +60,7 @@ void SampleLoadMeshData(SampleMeshData* meshData, SampleGpuMemory* gpuMemory) fread(meshBufferData, sizeof(uint8_t), meshData->MeshHeader.MeshBufferSizeInBytes, file); // TODO: Construct debug name - meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshData->MeshHeader.MeshBufferSizeInBytes, "TO_CHANGE"); + meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshData->MeshHeader.MeshBufferSizeInBytes, meshData->MeshHeader.Name); fclose(file); } diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 3311535a..504a595a 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -8,7 +8,11 @@ struct ShaderParameters uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; + uint32_t Reserved1; + float Scale; float3 Translation; + uint32_t Reserved2; + float4 Rotation; }; [[vk::push_constant]] @@ -43,19 +47,10 @@ struct VertexOutput }; #define IDENTITY_MATRIX float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) -// TODO: Put that in an include file -float4x4 TransformMatrix(float4 quaternion, float3 translation) -{ - float xw = quaternion.x*quaternion.w, xx = quaternion.x*quaternion.x, yy = quaternion.y*quaternion.y, - yw = quaternion.y*quaternion.w, xy = quaternion.x*quaternion.y, yz = quaternion.y*quaternion.z, - zw = quaternion.z*quaternion.w, xz = quaternion.x*quaternion.z, zz = quaternion.z*quaternion.z; - - float4 row1 = float4(1-2*(yy+zz), 2*(xy+zw), 2*(xz-yw), 0.0); - float4 row2 = float4( 2*(xy-zw),1-2*(xx+zz), 2*(yz+xw), 0.0); - float4 row3 = float4( 2*(xz+yw), 2*(yz-xw),1-2*(xx+yy), 0.0); - float4 row4 = float4(translation.x, translation.y, translation.z, 1.0); - return float4x4(row1, row2, row3, row4); +float3 RotateQuaternion(float3 v, float4 q) +{ + return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); } [shader("mesh")] @@ -79,17 +74,16 @@ void MeshMain(in uint groupId: SV_GroupID, ByteAddressBuffer frameDataBuffer = ResourceDescriptorHeap[parameters.FrameDataBufferIndex]; FrameData frameData = frameDataBuffer.Load(0); - float4x4 worldMatrix = TransformMatrix(float4(0, 0, 0, 1), parameters.Translation); - - float4x4 inverseTransposeWorldMatrix = worldMatrix; - uint vertexIndex = meshBuffer.Load(parameters.MeshletVertexIndexOffset + (meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); Vertex vertex = meshBuffer.Load(parameters.VertexBufferOffset + vertexIndex * sizeof(Vertex)); + float3 worldPosition = RotateQuaternion(vertex.Position, parameters.Rotation) * parameters.Scale + parameters.Translation; + float3 worldNormal = RotateQuaternion(vertex.Normal, parameters.Rotation); + //vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), mul(worldMatrix, frameData.ViewProjMatrix)); // NOTE: This calculation is faster because v * M is faster than M * M - vertices[groupThreadId].Position = mul(mul(float4(vertex.Position, 1.0), worldMatrix), frameData.ViewProjMatrix); - vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].Position = mul(float4(worldPosition, 1.0), frameData.ViewProjMatrix); + vertices[groupThreadId].WorldNormal = worldNormal; vertices[groupThreadId].MeshletIndex = groupId; } diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 5f977cf4..f5e1d7fa 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -18,8 +18,10 @@ typedef struct uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; uint32_t Reserved1; - uint32_t Reserved2; + float Scale; ElemVector3 Translation; + uint32_t Reserved2; + ElemVector4 Rotation; } ShaderParameters; typedef struct @@ -32,6 +34,8 @@ typedef struct typedef struct { SampleAppSettings AppSettings; + const char* ScenePath; + ElemWindow Window; ElemGraphicsDevice GraphicsDevice; ElemCommandQueue CommandQueue; @@ -105,13 +109,13 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues // TODO: Having GPU Upload is still annoying 😞 - applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64)); + applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(256)); applicationPayload->FrameDataBuffer = SampleCreateGpuBufferAndUploadData(&applicationPayload->GpuMemory, &applicationPayload->FrameData, sizeof(ShaderFrameData), "FrameData"); applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBuffer.ReadDescriptor; CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - SampleLoadScene("sponza.scene", &applicationPayload->TestSceneData); + SampleLoadScene(applicationPayload->ScenePath, &applicationPayload->TestSceneData); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -259,7 +263,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void applicationPayload->ShaderParameters.MeshletOffset = meshPrimitive->MeshletOffset; applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPrimitive->MeshletVertexIndexOffset; applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPrimitive->MeshletTriangleIndexOffset; + applicationPayload->ShaderParameters.Scale = sceneNode->Scale; applicationPayload->ShaderParameters.Translation = sceneNode->Translation; + applicationPayload->ShaderParameters.Rotation = sceneNode->Rotation; ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); ElemDispatchMesh(commandList, meshPrimitive->MeshletCount, 1, 1); @@ -288,9 +294,17 @@ int main(int argc, const char* argv[]) { ApplicationPayload payload = { - .AppSettings = SampleParseAppSettings(argc, argv) + .AppSettings = SampleParseAppSettings(argc, argv), + .ScenePath = "sponza.scene" }; + int32_t scenePathIndex = argc - 1; + + if (strstr(argv[scenePathIndex], ".scene")) + { + payload.ScenePath = argv[scenePathIndex]; + } + ElemConfigureLogHandler(ElemConsoleLogHandler); ElemRunApplication(&(ElemRunApplicationParameters) diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 0c6d6847..63fa6f0a 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -13,6 +13,8 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) { .MeshPrimitiveCount = mesh.MeshPrimitives.Length }; + + strncpy(meshHeader.Name, mesh.Name, 50); fwrite(&meshHeader, sizeof(SampleMeshHeader), 1, file); uint32_t meshPrimitiveHeadersOffset = ftell(file); @@ -26,8 +28,8 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) { ElemSceneMeshPrimitive* meshPrimitive = &mesh.MeshPrimitives.Items[i]; - // TODO: Index buffer - ElemBuildMeshletResult result = ElemBuildMeshlets(meshPrimitive->VertexBuffer, NULL); + // TODO: LOD! + ElemBuildMeshletResult result = ElemBuildMeshlets(meshPrimitive->VertexBuffer, meshPrimitive->IndexBuffer, NULL); DisplayOutputMessages("BuildMeshlets", result.Messages); @@ -91,13 +93,14 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) .NodeType = (SampleSceneNodeType)node->NodeType, .ReferenceIndex = node->ReferenceIndex, .Rotation = node->Rotation, + .Scale = node->Scale, .Translation = node->Translation }; strncpy(fileNode.Name, node->Name, 50); fwrite(&fileNode, sizeof(SampleSceneNodeHeader), 1, file); - printf("Node: '%s' MeshId=%d (T=%f,%f,%f)\n", node->Name, node->ReferenceIndex, node->Translation.X, node->Translation.Y, node->Translation.Z); + //printf("Node: '%s' MeshId=%d (T=%f,%f,%f)\n", node->Name, node->ReferenceIndex, node->Translation.X, node->Translation.Y, node->Translation.Z); } double beforeMeshlets = SampleGetTimerValueInMS(); @@ -182,9 +185,21 @@ int main(int argc, const char* argv[]) printf("File doesn't exist.\n"); return 1; } + + ElemLoadSceneOptions loadSceneOptions = {}; + // HACK: For now we hardcode options + if (strstr(inputPath, "sponza")) + { + loadSceneOptions.Scaling = 0.01f; + } + else if (strstr(inputPath, "bistro")) + { + //loadSceneOptions.CoordinateSystem = ElemSceneCoordinateSystem_RightHanded; + } + // TODO: Scaling should be passed as a parameter - ElemLoadSceneResult scene = ElemLoadScene(inputPath, &(ElemLoadSceneOptions) { .CoordinateSystem = ElemSceneCoordinateSystem_LeftHanded, .Scaling = 0.01f }); + ElemLoadSceneResult scene = ElemLoadScene(inputPath, &loadSceneOptions); DisplayOutputMessages("LoadScene", scene.Messages); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 59e0e4fc..22c19e01 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -531,6 +531,7 @@ ElemDataSpan VulkanGetGraphicsResourceDataSpan(ElemGraphicsResource resource) auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); SystemAssert(graphicsHeapData); + // TODO: use vkMapMemory2 just for consistency AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset, resourceData->Width, 0, &resourceData->CpuDataPointer)); } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 35118bd0..505125d3 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -892,6 +892,19 @@ typedef struct float X, Y, Z; } ElemVector3; +typedef union +{ + struct + { + float X, Y, Z, W; + }; + + struct + { + ElemVector3 XYZ; + }; +} ElemVector4; + typedef struct { ElemVector3 MinPoint; diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 8d2ccb6c..c19009aa 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -125,6 +125,25 @@ typedef struct float X, Y, Z; } ElemToolsVector3; +typedef union +{ + struct + { + float X, Y, Z, W; + }; + + struct + { + ElemToolsVector3 XYZ; + }; +} ElemToolsVector4; + +typedef union +{ + float m[4][4]; + ElemToolsVector4 Rows[4]; +} ElemToolsMatrix4x4; + typedef struct { ElemToolsVector3 MinPoint; @@ -237,6 +256,7 @@ typedef enum { ElemSceneFormat_Unknown = 0, ElemSceneFormat_Obj = 1, + ElemSceneFormat_Gltf = 2 } ElemSceneFormat; typedef enum @@ -255,6 +275,11 @@ typedef struct { ElemSceneCoordinateSystem CoordinateSystem; float Scaling; + ElemToolsVector3 Rotation; + ElemToolsVector3 Translation; + // TODO: Don't add options for the format here but in the builder instead + // we still want the same format mechanism but hardcoded + // TODO: Add options to specify the vertex format wanted } ElemLoadSceneOptions; @@ -275,6 +300,7 @@ typedef struct typedef struct { + const char* Name; ElemSceneMeshPrimitiveSpan MeshPrimitives; ElemToolsBoundingBox BoundingBox; // TODO: SphereVolume? @@ -291,7 +317,8 @@ typedef struct const char* Name; ElemSceneNodeType NodeType; int32_t ReferenceIndex; - ElemToolsVector3 Rotation; + ElemToolsVector4 Rotation; + float Scale; ElemToolsVector3 Translation; // TODO: Children } ElemSceneNode; @@ -325,7 +352,6 @@ ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadS typedef struct { // TODO: Allow bypass mesh format - // TODO: Support index buffers uint32_t Reserved; } ElemBuildMeshletsOptions; @@ -356,7 +382,7 @@ typedef struct bool HasErrors; } ElemBuildMeshletResult; -ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options); +ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemUInt32Span indexBuffer, const ElemBuildMeshletsOptions* options); // TODO: Do an optimize mesh function that can be used to optimize a normal mesh (for Raytracing for example) diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index 7ae2db8a..e5030190 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -27,7 +27,7 @@ typedef struct ElementalToolsFunctions bool (*ElemCanCompileShader)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform); ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *); ElemLoadSceneResult (*ElemLoadScene)(char const *, ElemLoadSceneOptions const *); - ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemBuildMeshletsOptions const *); + ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *); } ElementalToolsFunctions; @@ -84,7 +84,7 @@ static bool LoadElementalToolsFunctionPointers(void) listElementalToolsFunctions.ElemCanCompileShader = (bool (*)(ElemShaderLanguage, ElemToolsGraphicsApi, ElemToolsPlatform))GetElementalToolsFunctionPointer("ElemCanCompileShader"); listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); listElementalToolsFunctions.ElemLoadScene = (ElemLoadSceneResult (*)(char const *, ElemLoadSceneOptions const *))GetElementalToolsFunctionPointer("ElemLoadScene"); - listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); + listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); functionPointersLoadedElementalTools = 1; @@ -201,7 +201,7 @@ static inline ElemLoadSceneResult ElemLoadScene(char const * path, ElemLoadScene return listElementalToolsFunctions.ElemLoadScene(path, options); } -static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemBuildMeshletsOptions const * options) +static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemUInt32Span indexBuffer, ElemBuildMeshletsOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { @@ -229,5 +229,5 @@ static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBu return result; } - return listElementalToolsFunctions.ElemBuildMeshlets(vertexBuffer, options); + return listElementalToolsFunctions.ElemBuildMeshlets(vertexBuffer, indexBuffer, options); } diff --git a/src/ElementalTools/Meshes/MeshletBuilder.cpp b/src/ElementalTools/Meshes/MeshletBuilder.cpp index a79714b4..c9680bcf 100644 --- a/src/ElementalTools/Meshes/MeshletBuilder.cpp +++ b/src/ElementalTools/Meshes/MeshletBuilder.cpp @@ -14,7 +14,7 @@ void InitMeshletBuilderMemoryArena() SystemClearMemoryArena(MeshletBuilderMemoryArena); } -ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, const ElemBuildMeshletsOptions* options) +ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuffer, ElemUInt32Span indexBuffer, const ElemBuildMeshletsOptions* options) { InitMeshletBuilderMemoryArena(); @@ -22,17 +22,24 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto stackMemoryArena = SystemGetStackMemoryArena(); - // TODO: Take into account the index buffer if present auto indexCount = vertexBuffer.Data.Length / vertexBuffer.VertexSize; + uint32_t* indexBufferPointer = nullptr; + + if (indexBuffer.Length > 0) + { + indexBufferPointer = indexBuffer.Items; + indexCount = indexBuffer.Length; + } + auto vertexRemap = SystemPushArrayZero(stackMemoryArena, indexCount); - auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, nullptr, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); + auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, indexBufferPointer, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); auto vertexList = SystemPushArrayZero(MeshletBuilderMemoryArena, vertexCount * vertexBuffer.VertexSize); auto indexList = SystemPushArrayZero(stackMemoryArena, indexCount); meshopt_remapVertexBuffer(vertexList.Pointer, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize, vertexRemap.Pointer); - meshopt_remapIndexBuffer(indexList.Pointer, nullptr, indexCount, vertexRemap.Pointer); + meshopt_remapIndexBuffer(indexList.Pointer, indexBufferPointer, indexCount, vertexRemap.Pointer); meshopt_optimizeVertexCache(indexList.Pointer, indexList.Pointer, indexCount, vertexCount); meshopt_optimizeVertexFetch(vertexList.Pointer, indexList.Pointer, indexCount, vertexList.Pointer, vertexCount, vertexBuffer.VertexSize); diff --git a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h index 0f1cfb22..75357e68 100644 --- a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h @@ -33,10 +33,13 @@ #include "dxcapi.h" #include "d3d12shader.h" -#include "meshoptimizer.h" #include "metal_irconverter/metal_irconverter.h" +#include "meshoptimizer.h" +#include "fast_obj.h" +#include "cgltf.h" + #ifdef _WASDEBUG #define _DEBUG #endif diff --git a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h index 839509a6..0a9c4d60 100644 --- a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h @@ -37,6 +37,8 @@ #include "d3d12shader.h" #include "meshoptimizer.h" +#include "fast_obj.h" +#include "cgltf.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h index c5553c62..cbfa4fe5 100644 --- a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h @@ -38,6 +38,7 @@ using namespace Microsoft::WRL; #include "meshoptimizer.h" #include "fast_obj.h" +#include "cgltf.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/SceneLoading/SceneLoader.cpp b/src/ElementalTools/SceneLoading/SceneLoader.cpp index 4d2f6559..6603e2c4 100644 --- a/src/ElementalTools/SceneLoading/SceneLoader.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoader.cpp @@ -1,6 +1,6 @@ -#include "ElementalTools.h" -#include "SystemMemory.h" +#include "SceneLoader.h" #include "SceneLoaderObj.cpp" +#include "SceneLoaderGltf.cpp" // TODO: Do one for each thread static MemoryArena SceneLoaderMemoryArena; @@ -19,6 +19,21 @@ MemoryArena GetSceneLoaderMemoryArena() { return SceneLoaderMemoryArena; } + +ElemToolsMatrix4x4 CreateSceneLoaderGlobalTransformMatrix(const ElemLoadSceneOptions* options) +{ + auto result = ElemToolsCreateIdentityMatrix(); + + ElemToolsVector4 rotationQuaternion = ElemToolsMulQuat(ElemToolsCreateQuaternion((ElemToolsVector3){ 1, 0, 0 }, options->Rotation.X), + ElemToolsMulQuat(ElemToolsCreateQuaternion((ElemToolsVector3){ 0, 0, 1 }, options->Rotation.Z), + ElemToolsCreateQuaternion((ElemToolsVector3){ 0, 1, 0 }, options->Rotation.Y))); + + result = ElemToolsMulMatrix4x4(ElemToolsCreateRotationMatrix(rotationQuaternion), result); + result = ElemToolsMulMatrix4x4(ElemToolsCreateScaleMatrix(options->Scaling), result); + result = ElemToolsMulMatrix4x4(ElemToolsCreateTranslationMatrix(options->Translation), result); + + return result; +} ElemSceneFormat GetSceneFormatFromPath(const char* path) { @@ -35,6 +50,10 @@ ElemSceneFormat GetSceneFormatFromPath(const char* path) { return ElemSceneFormat_Obj; } + else if (SystemFindSubString(extension, "gltf") != -1) + { + return ElemSceneFormat_Gltf; + } return ElemSceneFormat_Unknown; } @@ -63,6 +82,9 @@ ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadS case ElemSceneFormat_Obj: return LoadObjScene(path, &loadSceneOptions); + case ElemSceneFormat_Gltf: + return LoadGltfScene(path, &loadSceneOptions); + default: return { diff --git a/src/ElementalTools/SceneLoading/SceneLoader.h b/src/ElementalTools/SceneLoading/SceneLoader.h index 2a84f81d..f286de28 100644 --- a/src/ElementalTools/SceneLoading/SceneLoader.h +++ b/src/ElementalTools/SceneLoading/SceneLoader.h @@ -1,5 +1,7 @@ #pragma once +#include "ElementalTools.h" #include "SystemMemory.h" MemoryArena GetSceneLoaderMemoryArena(); +ElemToolsMatrix4x4 CreateSceneLoaderGlobalTransformMatrix(const ElemLoadSceneOptions* options); diff --git a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp new file mode 100644 index 00000000..554394ab --- /dev/null +++ b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp @@ -0,0 +1,275 @@ +#include "SceneLoader.h" +#include "ToolsUtils.h" +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + +cgltf_result ClgtfFileRead(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, const char* path, cgltf_size* size, void** data) +{ + auto objFileData = LoadFileData(path); + + if (objFileData.Length == 0) + { + return cgltf_result_io_error; + } + + *data = (uint8_t*)objFileData.Pointer; + *size = objFileData.Length; + + return cgltf_result_success; +} + +void ClgtfFileRelease(const struct cgltf_memory_options* memory_options, const struct cgltf_file_options* file_options, void* data) +{ +} + +ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_primitive* gltfPrimitive, ElemSceneCoordinateSystem coordinateSystem, ElemToolsBoundingBox* boundingBox) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + // TODO: Config of the vertex components to load + // TODO: Support vertex compression (eg: float16 for positions?) + // TODO: Tangent + auto positionSize = sizeof(float) * 3; + auto normalSize = sizeof(float) * 3; + auto textureCoordinatesSize = sizeof(float) * 2; + auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; + + // TODO: Temporary + auto realVertexSize = maxVertexSize; + auto vertexCount = gltfPrimitive->attributes[0].data->count; + + // TODO: Combine that with the components selection + auto positionAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_position, 0); + auto normalAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_normal, 0); + auto textureCoordinatesAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_texcoord, 0); + + // TODO: Rework this + auto gltfPositionBuffer = SystemPushArray(stackMemoryArena, vertexCount * 3); + auto gltfNormalBuffer = SystemPushArray(stackMemoryArena, vertexCount * 3); + auto gltfTextureCoordinatesBuffer = SystemPushArray(stackMemoryArena, vertexCount * 2); + + SystemAssert(positionAccessor); + cgltf_accessor_unpack_floats(positionAccessor, gltfPositionBuffer.Pointer, gltfPositionBuffer.Length); + + if (normalAccessor) + { + cgltf_accessor_unpack_floats(normalAccessor, gltfNormalBuffer.Pointer, gltfNormalBuffer.Length); + } + + if (textureCoordinatesAccessor) + { + cgltf_accessor_unpack_floats(textureCoordinatesAccessor, gltfTextureCoordinatesBuffer.Pointer, gltfTextureCoordinatesBuffer.Length); + } + + auto vertexBuffer = SystemPushArray(memoryArena, vertexCount * maxVertexSize); + auto currentVertexBufferPointer = vertexBuffer.Pointer; + + for (uint32_t i = 0; i < vertexCount; i++) + { + // Position + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = gltfPositionBuffer[i * 3 + j]; + } + + AddPointToBoundingBox({ ((float*)currentVertexBufferPointer)[0], ((float*)currentVertexBufferPointer)[1], ((float*)currentVertexBufferPointer)[2]}, boundingBox); + currentVertexBufferPointer += 3 * sizeof(float); + + // Normal + for (uint32_t j = 0; j < 3; j++) + { + ((float*)currentVertexBufferPointer)[j] = gltfNormalBuffer[i * 3 + j]; + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + } + + currentVertexBufferPointer += 3 * sizeof(float); + + // Texture coordinates + for (uint32_t j = 0; j < 2; j++) + { + ((float*)currentVertexBufferPointer)[j] = gltfTextureCoordinatesBuffer[i * 2 + j]; + } + + currentVertexBufferPointer += 2 * sizeof(float); + } + + return + { + .Data = { .Items = vertexBuffer.Pointer, .Length = (uint32_t)vertexBuffer.Length }, + .VertexSize = (uint32_t)realVertexSize, + .VertexCount = (uint32_t)vertexCount + }; +} + +ElemUInt32Span ConstructGltfIndexBuffer(MemoryArena memoryArena, const cgltf_primitive* gltfPrimitive, ElemSceneCoordinateSystem coordinateSystem) +{ + auto indexBuffer = SystemPushArray(memoryArena, gltfPrimitive->indices->count); + cgltf_accessor_unpack_indices(gltfPrimitive->indices, indexBuffer.Pointer, sizeof(uint32_t), indexBuffer.Length); + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + for (uint32_t i = 0; i < indexBuffer.Length; i += 3) + { + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + uint32_t temp = indexBuffer[i + 1]; + indexBuffer[i + 1] = indexBuffer[i + 2]; + indexBuffer[i + 2] = temp; + } + } + } + + return { .Items = indexBuffer.Pointer, .Length = (uint32_t)indexBuffer.Length }; +} + +ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); + + auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); + + auto messages = SystemPushArray(sceneLoaderMemoryArena, 1024); + auto messageCount = 0u; + auto hasErrors = false; + + cgltf_options cgltfOptions = + { + .file = + { + .read = ClgtfFileRead, + .release = ClgtfFileRelease + } + }; + + cgltf_data* data = NULL; + cgltf_result result = cgltf_parse_file(&cgltfOptions, path, &data); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while reading glTF file."), .HasErrors = true }; + } + + result = cgltf_load_buffers(&cgltfOptions, data, path); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while loading glTF buffers."), .HasErrors = true }; + } + + result = cgltf_validate(data); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while validating glTF file."), .HasErrors = true }; + } + + auto meshes = SystemPushArray(sceneLoaderMemoryArena, data->meshes_count); + auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, data->nodes_count); + + for (uint32_t i = 0; i < data->meshes_count; i++) + { + auto mesh = &meshes[i]; + auto gltfMesh = &data->meshes[i]; + + mesh->BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } + }; + + auto meshPrimitives = SystemPushArray(sceneLoaderMemoryArena, gltfMesh->primitives_count); + + for (uint32_t j = 0; j < gltfMesh->primitives_count; j++) + { + auto meshPrimitive = &meshPrimitives[j]; + auto gltfPrimitive = &gltfMesh->primitives[j]; + + if (gltfPrimitive->type != cgltf_primitive_type_triangles) + { + messages[messageCount++] = + { + .Type = ElemToolsMessageType_Warning, + .Message = "glTF loader only support triangles for now." + }; + + continue; + } + + if (!cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_position, 0)) + { + messages[messageCount++] = + { + .Type = ElemToolsMessageType_Error, + .Message = "The mesh primitive doesn't contains a position attribute." + }; + + continue; + } + + meshPrimitive->BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } + }; + + meshPrimitive->VertexBuffer = ConstructGltfVertexBuffer(sceneLoaderMemoryArena, gltfPrimitive, options->CoordinateSystem, &meshPrimitive->BoundingBox); + meshPrimitive->IndexBuffer = ConstructGltfIndexBuffer(sceneLoaderMemoryArena, gltfPrimitive, options->CoordinateSystem); + + AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); + } + + mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(gltfMesh->name)).Pointer; + mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; + } + + for (uint32_t i = 0; i < data->nodes_count; i++) + { + auto sceneNode = &sceneNodes[i]; + auto gltfNode = &data->nodes[i]; + printf("Test: %s\n", gltfNode->name); + + sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(gltfNode->name)).Pointer; + sceneNode->Rotation = {}; + sceneNode->Translation = {}; + + if (gltfNode->mesh) + { + sceneNode->NodeType = ElemSceneNodeType_Mesh; + sceneNode->ReferenceIndex = cgltf_mesh_index(data, gltfNode->mesh); + } + + float matrix[16]; + cgltf_node_transform_world(gltfNode, matrix); + ElemToolsMatrix4x4 nodeTransform = *(ElemToolsMatrix4x4*)matrix; + + if (options->CoordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + auto flipMatrix = ElemToolsCreateIdentityMatrix(); + flipMatrix.Rows[2].Z = -1; + + nodeTransform = ElemToolsMulMatrix4x4(nodeTransform, flipMatrix); + } + + nodeTransform = ElemToolsMulMatrix4x4(nodeTransform, globalTransformMatrix); + DecomposeTransform(nodeTransform, &sceneNode->Scale, &sceneNode->Rotation, &sceneNode->Translation); + } + + cgltf_free(data); + ResetLoadFileDataMemory(); + + return + { + .SceneFormat = ElemSceneFormat_Gltf, + .CoordinateSystem = options->CoordinateSystem, + .Meshes = { .Items = meshes.Pointer, .Length = (uint32_t)meshes.Length }, + .Nodes = { .Items = sceneNodes.Pointer, .Length = (uint32_t)sceneNodes.Length }, + .Messages = { .Items = messages.Pointer, .Length = messageCount }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index df143390..345d57fd 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -17,44 +17,6 @@ struct ObjMeshPrimitiveInfo ElemToolsBoundingBox BoundingBox; }; -void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundingBox) -{ - float minX = (point.X < boundingBox->MinPoint.X) ? point.X : boundingBox->MinPoint.X; - float minY = (point.Y < boundingBox->MinPoint.Y) ? point.Y : boundingBox->MinPoint.Y; - float minZ = (point.Z < boundingBox->MinPoint.Z) ? point.Z : boundingBox->MinPoint.Z; - - float maxX = (point.X > boundingBox->MaxPoint.X) ? point.X : boundingBox->MaxPoint.X; - float maxY = (point.Y > boundingBox->MaxPoint.Y) ? point.Y : boundingBox->MaxPoint.Y; - float maxZ = (point.Z > boundingBox->MaxPoint.Z) ? point.Z : boundingBox->MaxPoint.Z; - - boundingBox->MinPoint = { minX, minY, minZ }; - boundingBox->MaxPoint = { maxX, maxY, maxZ }; -} - -void AddBoundingBoxToBoundingBox(const ElemToolsBoundingBox* additional, ElemToolsBoundingBox* boundingBox) -{ - float minX = SystemMin(boundingBox->MinPoint.X, additional->MinPoint.X); - float minY = SystemMin(boundingBox->MinPoint.Y, additional->MinPoint.Y); - float minZ = SystemMin(boundingBox->MinPoint.Z, additional->MinPoint.Z); - - float maxX = SystemMax(boundingBox->MaxPoint.X, additional->MaxPoint.X); - float maxY = SystemMax(boundingBox->MaxPoint.Y, additional->MaxPoint.Y); - float maxZ = SystemMax(boundingBox->MaxPoint.Z, additional->MaxPoint.Z); - - boundingBox->MinPoint = { minX, minY, minZ }; - boundingBox->MaxPoint = { maxX, maxY, maxZ }; -} - -ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox) -{ - return - { - .X = (boundingBox->MinPoint.X + boundingBox->MaxPoint.X) * 0.5f, - .Y = (boundingBox->MinPoint.Y + boundingBox->MaxPoint.Y) * 0.5f, - .Z = (boundingBox->MinPoint.Z + boundingBox->MaxPoint.Z) * 0.5f - }; -} - void* FastObjFileOpen(const char* path, void* userData) { auto objFileData = LoadFileData(path); @@ -102,7 +64,8 @@ unsigned long FastObjFileSize(void* file, void* userData) return objLoaderFileData->FileData.Length; } -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, float scaling, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) +// TODO: Remove that function and include it in construct vertex buffer +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) { // TODO: Check what to include auto currentVertexBufferPointer = *vertexBufferPointer; @@ -111,7 +74,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc { for (uint32_t j = 0; j < 3; j++) { - ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j] * scaling; + ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; } if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) @@ -151,12 +114,8 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo, const ElemLoadSceneOptions* options) +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) { - SystemAssert(options); - auto coordinateSystem = options->CoordinateSystem; - auto scaling = options->Scaling; - // TODO: Config of the vertex components to load auto positionSize = sizeof(float) * 3; auto normalSize = sizeof(float) * 3; @@ -169,20 +128,9 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj auto vertexBuffer = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount * maxVertexSize); auto currentVertexBufferPointer = vertexBuffer.Pointer; - for (uint32_t i = meshPrimitiveInfo->IndexOffset; i < meshPrimitiveInfo->IndexOffset + meshPrimitiveInfo->IndexCount; i += 3) + for (uint32_t i = meshPrimitiveInfo->IndexOffset; i < meshPrimitiveInfo->IndexOffset + meshPrimitiveInfo->IndexCount; i++) { - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) - { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - } - else - { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - ProcessObjVertex(objMesh->indices[i + 1], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - ProcessObjVertex(objMesh->indices[i + 2], objMesh, coordinateSystem, scaling, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - } + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); } return @@ -193,6 +141,29 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, const fastObj }; } +ElemUInt32Span ConstructObjIndexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) +{ + auto indexBuffer = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount); + + for (uint32_t i = 0; i < meshPrimitiveInfo->IndexCount; i += 3) + { + indexBuffer[i] = i; + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + indexBuffer[i + 1] = i + 2; + indexBuffer[i + 2] = i + 1; + } + else + { + indexBuffer[i + 1] = i + 1; + indexBuffer[i + 2] = i + 2; + } + } + + return { .Items = indexBuffer.Pointer, .Length = (uint32_t)indexBuffer.Length }; +} + void ApplyObjMeshPrimitiveInverseTranslation(ElemToolsVector3 translation, ElemVertexBuffer* vertexBuffer) { for (uint32_t i = 0; i < vertexBuffer->VertexCount; i++) @@ -213,6 +184,8 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto stackMemoryArena = SystemGetStackMemoryArena(); auto hasErrors = false; + auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); + auto callbacks = fastObjCallbacks { .file_open = FastObjFileOpen, @@ -318,7 +291,8 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto meshPrimitive = &meshPrimitives[j]; auto meshPrimitiveInfo = &meshPrimitiveInfos[j]; - meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, objFileData, meshPrimitiveInfo, options); + meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); + meshPrimitive->IndexBuffer = ConstructObjIndexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); meshPrimitive->BoundingBox = meshPrimitiveInfo->BoundingBox; AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); @@ -334,16 +308,22 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o ApplyObjMeshPrimitiveInverseTranslation(boundingBoxCenter, &meshPrimitive->VertexBuffer); ApplyObjBoundingBoxInverseTranslation(boundingBoxCenter, &meshPrimitive->BoundingBox); } - + + mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; sceneNode->NodeType = ElemSceneNodeType_Mesh; sceneNode->ReferenceIndex = i; - sceneNode->Rotation = {}; - sceneNode->Translation = boundingBoxCenter; + + auto nodeTransform = ElemToolsCreateTranslationMatrix({ boundingBoxCenter.X, boundingBoxCenter.Y, boundingBoxCenter.Z }); + + nodeTransform = ElemToolsMulMatrix4x4(nodeTransform, globalTransformMatrix); + DecomposeTransform(nodeTransform, &sceneNode->Scale, &sceneNode->Rotation, &sceneNode->Translation); } + ResetLoadFileDataMemory(); + return { .SceneFormat = ElemSceneFormat_Obj, diff --git a/src/ElementalTools/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp index 4c080b72..8749b40c 100644 --- a/src/ElementalTools/ToolsUtils.cpp +++ b/src/ElementalTools/ToolsUtils.cpp @@ -58,3 +58,226 @@ ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const ch .Length = 1 }; } + +void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundingBox) +{ + float minX = (point.X < boundingBox->MinPoint.X) ? point.X : boundingBox->MinPoint.X; + float minY = (point.Y < boundingBox->MinPoint.Y) ? point.Y : boundingBox->MinPoint.Y; + float minZ = (point.Z < boundingBox->MinPoint.Z) ? point.Z : boundingBox->MinPoint.Z; + + float maxX = (point.X > boundingBox->MaxPoint.X) ? point.X : boundingBox->MaxPoint.X; + float maxY = (point.Y > boundingBox->MaxPoint.Y) ? point.Y : boundingBox->MaxPoint.Y; + float maxZ = (point.Z > boundingBox->MaxPoint.Z) ? point.Z : boundingBox->MaxPoint.Z; + + boundingBox->MinPoint = { minX, minY, minZ }; + boundingBox->MaxPoint = { maxX, maxY, maxZ }; +} + +void AddBoundingBoxToBoundingBox(const ElemToolsBoundingBox* additional, ElemToolsBoundingBox* boundingBox) +{ + float minX = SystemMin(boundingBox->MinPoint.X, additional->MinPoint.X); + float minY = SystemMin(boundingBox->MinPoint.Y, additional->MinPoint.Y); + float minZ = SystemMin(boundingBox->MinPoint.Z, additional->MinPoint.Z); + + float maxX = SystemMax(boundingBox->MaxPoint.X, additional->MaxPoint.X); + float maxY = SystemMax(boundingBox->MaxPoint.Y, additional->MaxPoint.Y); + float maxZ = SystemMax(boundingBox->MaxPoint.Z, additional->MaxPoint.Z); + + boundingBox->MinPoint = { minX, minY, minZ }; + boundingBox->MaxPoint = { maxX, maxY, maxZ }; +} + +ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox) +{ + return + { + .X = (boundingBox->MinPoint.X + boundingBox->MaxPoint.X) * 0.5f, + .Y = (boundingBox->MinPoint.Y + boundingBox->MaxPoint.Y) * 0.5f, + .Z = (boundingBox->MinPoint.Z + boundingBox->MaxPoint.Z) * 0.5f + }; +} + +// TODO: CLEANUP +ElemToolsVector3 ElemToolsCrossProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2) +{ + ElemToolsVector3 result; + result.X = v1.Y * v2.Z - v1.Z * v2.Y; + result.Y = v1.Z * v2.X - v1.X * v2.Z; + result.Z = v1.X * v2.Y - v1.Y * v2.X; + return result; +} + +ElemToolsVector4 ElemToolsCreateQuaternion(ElemToolsVector3 v, float w) +{ + ElemToolsVector4 result; + + result.X = v.X * sinf(w * 0.5f); + result.Y = v.Y * sinf(w * 0.5f); + result.Z = v.Z * sinf(w * 0.5f); + result.W = cosf(w * 0.5f); + + return result; +} + +float ElemToolsDotProductQuat(ElemToolsVector4 v1, ElemToolsVector4 v2) +{ + return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z; +} + +ElemToolsVector4 ElemToolsMulQuat(ElemToolsVector4 q1, ElemToolsVector4 q2) +{ + float x = q2.X * q1.W + q1.X * q2.W + ElemToolsCrossProductV3(q1.XYZ, q2.XYZ).X; + float y = q2.Y * q1.W + q1.Y * q2.W + ElemToolsCrossProductV3(q1.XYZ, q2.XYZ).Y; + float z = q2.Z * q1.W + q1.Z * q2.W + ElemToolsCrossProductV3(q1.XYZ, q2.XYZ).Z; + + float w = q1.W * q2.W - ElemToolsDotProductQuat(q1, q2); + + return (ElemToolsVector4) { .X = x, .Y = y, .Z = z, .W = w }; +} + +ElemToolsMatrix4x4 ElemToolsCreateIdentityMatrix() +{ + ElemToolsMatrix4x4 result; + + result.m[0][0] = 1.0f; + result.m[0][1] = 0.0f; + result.m[0][2] = 0.0f; + result.m[0][3] = 0.0f; + + result.m[1][0] = 0.0f; + result.m[1][1] = 1.0f; + result.m[1][2] = 0.0f; + result.m[1][3] = 0.0f; + + result.m[2][0] = 0.0f; + result.m[2][1] = 0.0f; + result.m[2][2] = 1.0f; + result.m[2][3] = 0.0f; + + result.m[3][0] = 0.0f; + result.m[3][1] = 0.0f; + result.m[3][2] = 0.0f; + result.m[3][3] = 1.0f; + + return result; +} + +ElemToolsMatrix4x4 ElemToolsCreateScaleMatrix(float scale) +{ + ElemToolsMatrix4x4 result; + + result.Rows[0] = { scale, 0.0f, 0.0f, 0.0f }; + result.Rows[1] = { 0.0f, scale, 0.0f, 0.0f }; + result.Rows[2] = { 0.0f, 0.0f, scale, 0.0f }; + result.Rows[3] = { 0.0f, 0.0f, 0.0f, 1.0f }; + + return result; +} + +ElemToolsMatrix4x4 ElemToolsCreateTranslationMatrix(ElemToolsVector3 translation) +{ + ElemToolsMatrix4x4 result; + + result.Rows[0] = { 1.0f, 0.0f, 0.0f, 0.0f }; + result.Rows[1] = { 0.0f, 1.0f, 0.0f, 0.0f }; + result.Rows[2] = { 0.0f, 0.0f, 1.0f, 0.0f }; + result.Rows[3] = { translation.X, translation.Y, translation.Z, 1.0f }; + + return result; +} + +ElemToolsMatrix4x4 ElemToolsMulMatrix4x4(ElemToolsMatrix4x4 a, ElemToolsMatrix4x4 b) +{ + ElemToolsMatrix4x4 result; + + for (uint32_t i = 0; i < 4; i++) + { + for (uint32_t j = 0; j < 4; j++) + { + result.m[i][j] = a.m[i][0] * b.m[0][j] + a.m[i][1] * b.m[1][j] + a.m[i][2] * b.m[2][j] + a.m[i][3] * b.m[3][j]; + } + } + + return result; +} + +ElemToolsMatrix4x4 ElemToolsCreateRotationMatrix(ElemToolsVector4 quaternion) +{ + float xw = quaternion.X *quaternion.W , xx = quaternion.X *quaternion.X , yy = quaternion.Y *quaternion.Y , + yw = quaternion.Y *quaternion.W , xy = quaternion.X *quaternion.Y , yz = quaternion.Y *quaternion.Z , + zw = quaternion.Z *quaternion.W , xz = quaternion.X *quaternion.Z , zz = quaternion.Z *quaternion.Z ; + + ElemToolsMatrix4x4 result; + + result.Rows[0] = (ElemToolsVector4) { .X = 1-2*(yy+zz), .Y = 2*(xy+zw), .Z = 2*(xz-yw), .W = 0.0f }; + result.Rows[1] = (ElemToolsVector4) { .X = 2*(xy-zw), .Y = 1-2*(xx+zz), .Z = 2*(yz+xw), .W = 0.0f }; + result.Rows[2] = (ElemToolsVector4) { .X = 2*(xz+yw), .Y = 2*(yz-xw), .Z = 1-2*(xx+yy), .W = 0.0f }; + result.Rows[3] = (ElemToolsVector4) { .X = 0.0f, .Y = 0.0f, .Z = 0.0f, .W = 1.0f }; + + return result; +} + +ElemToolsVector3 ElemToolsTransformPointV3(ElemToolsVector3 point, ElemToolsMatrix4x4 m) +{ + ElemToolsVector3 result; + + result.X = m.m[0][0] * point.X + m.m[0][1] * point.Y + m.m[0][2] * point.Z + m.m[0][3]; + result.Y = m.m[1][0] * point.X + m.m[1][1] * point.Y + m.m[1][2] * point.Z + m.m[1][3]; + result.Z = m.m[2][0] * point.X + m.m[2][1] * point.Y + m.m[2][2] * point.Z + m.m[2][3]; + + return result; +} + +void DecomposeTransform(ElemToolsMatrix4x4 transform, float* scale, ElemToolsVector4* rotationQuaternion, ElemToolsVector3* translation) +{ + translation->X = transform.Rows[3].X; + translation->Y = transform.Rows[3].Y; + translation->Z = transform.Rows[3].Z; + + // compute determinant to determine handedness + float det = + transform.m[0][0] * (transform.m[1][1] * transform.m[2][2] - transform.m[2][1] * transform.m[1][2]) - + transform.m[0][1] * (transform.m[1][0] * transform.m[2][2] - transform.m[1][2] * transform.m[2][0]) + + transform.m[0][2] * (transform.m[1][0] * transform.m[2][1] - transform.m[1][1] * transform.m[2][0]); + + float sign = (det < 0.f) ? -1.f : 1.f; + + // recover scale from axis lengths + float scales[3]; + + scales[0] = sqrtf(transform.m[0][0] * transform.m[0][0] + transform.m[0][1] * transform.m[0][1] + transform.m[0][2] * transform.m[0][2]) * sign; + scales[1] = sqrtf(transform.m[1][0] * transform.m[1][0] + transform.m[1][1] * transform.m[1][1] + transform.m[1][2] * transform.m[1][2]) * sign; + scales[2] = sqrtf(transform.m[2][0] * transform.m[2][0] + transform.m[2][1] * transform.m[2][1] + transform.m[2][2] * transform.m[2][2]) * sign; + + *scale = scales[0]; + + // normalize axes to get a pure rotation matrix + float rsx = (scales[0] == 0.f) ? 0.f : 1.f / scales[0]; + float rsy = (scales[1] == 0.f) ? 0.f : 1.f / scales[1]; + float rsz = (scales[2] == 0.f) ? 0.f : 1.f / scales[2]; + + float r00 = transform.m[0][0] * rsx, r10 = transform.m[1][0] * rsy, r20 = transform.m[2][0] * rsz; + float r01 = transform.m[0][1] * rsx, r11 = transform.m[1][1] * rsy, r21 = transform.m[2][1] * rsz; + float r02 = transform.m[0][2] * rsx, r12 = transform.m[1][2] * rsy, r22 = transform.m[2][2] * rsz; + + // "branchless" version of Mike Day's matrix to quaternion conversion + int qc = r22 < 0 ? (r00 > r11 ? 0 : 1) : (r00 < -r11 ? 2 : 3); + float qs1 = qc & 2 ? -1.f : 1.f; + float qs2 = qc & 1 ? -1.f : 1.f; + float qs3 = (qc - 1) & 2 ? -1.f : 1.f; + + float qt = 1.f - qs3 * r00 - qs2 * r11 - qs1 * r22; + float qs = 0.5f / sqrtf(qt); + + float* rotation = (float*)rotationQuaternion; + + rotation[qc ^ 0] = qs * qt; + rotation[qc ^ 1] = qs * (r01 + qs1 * r10); + rotation[qc ^ 2] = qs * (r20 + qs2 * r02); + rotation[qc ^ 3] = qs * (r12 + qs3 * r21); + + rotationQuaternion->X = rotation[0]; + rotationQuaternion->Y = rotation[1]; + rotationQuaternion->Z = rotation[2]; + rotationQuaternion->W = rotation[3]; +} diff --git a/src/ElementalTools/ToolsUtils.h b/src/ElementalTools/ToolsUtils.h index 1cad9b9b..6ce9874e 100644 --- a/src/ElementalTools/ToolsUtils.h +++ b/src/ElementalTools/ToolsUtils.h @@ -8,3 +8,18 @@ ReadOnlySpan LoadFileData(const char* path); void ResetLoadFileDataMemory(); ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const char* errorMessage); + +void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundingBox); +void AddBoundingBoxToBoundingBox(const ElemToolsBoundingBox* additional, ElemToolsBoundingBox* boundingBox); +ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox); + +ElemToolsVector4 ElemToolsCreateQuaternion(ElemToolsVector3 v, float w); +ElemToolsVector4 ElemToolsMulQuat(ElemToolsVector4 q1, ElemToolsVector4 q2); +ElemToolsMatrix4x4 ElemToolsCreateRotationMatrix(ElemToolsVector4 quaternion); + +ElemToolsMatrix4x4 ElemToolsCreateIdentityMatrix(); +ElemToolsMatrix4x4 ElemToolsCreateScaleMatrix(float scale); +ElemToolsMatrix4x4 ElemToolsCreateTranslationMatrix(ElemToolsVector3 translation); +ElemToolsMatrix4x4 ElemToolsMulMatrix4x4(ElemToolsMatrix4x4 a, ElemToolsMatrix4x4 b); + +void DecomposeTransform(ElemToolsMatrix4x4 transform, float* scale, ElemToolsVector4* rotationQuaternion, ElemToolsVector3* translation); diff --git a/src/ElementalTools/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp index 47848a28..438bc538 100644 --- a/src/ElementalTools/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -7,6 +7,9 @@ #include "Shaders/MetalShaderConverter.cpp" #endif +#define CGLTF_IMPLEMENTATION +#include "cgltf.h" + #include "fast_obj.c" #include "SceneLoading/SceneLoader.cpp" diff --git a/tests/ToolsTests/MeshBuilderTests.cpp b/tests/ToolsTests/MeshBuilderTests.cpp index c7c6bae4..b19e44ba 100644 --- a/tests/ToolsTests/MeshBuilderTests.cpp +++ b/tests/ToolsTests/MeshBuilderTests.cpp @@ -43,15 +43,30 @@ ElemVertexBuffer TestBuildVertexBuffer(TestMeshVertex* destination, uint32_t cou }; } +ElemUInt32Span TestBuildIndexBuffer(uint32_t* destination, uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) + { + destination[i] = i; + } + + return + { + .Items = destination, .Length = count + }; +} + UTEST(MeshBuilder, CheckVertexBuffer) { // Arrange const uint32_t vertexCount = 210; TestMeshVertex vertexList[vertexCount]; + uint32_t indexList[vertexCount / 3]; auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); // Act - auto result = ElemBuildMeshlets(vertexBuffer, NULL); + auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); // Assert ASSERT_FALSE(result.HasErrors); @@ -89,7 +104,9 @@ UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) // Arrange const uint32_t vertexCount = 210; TestMeshVertex vertexList[vertexCount]; + uint32_t indexList[vertexCount / 3]; auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); for (uint32_t i = 0; i < 3; i++) { @@ -99,7 +116,7 @@ UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) vertexBuffer.Data.Length += vertexBuffer.VertexSize * 3; // Act - auto result = ElemBuildMeshlets(vertexBuffer, NULL); + auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); // Assert ASSERT_FALSE(result.HasErrors); @@ -112,10 +129,12 @@ UTEST(MeshBuilder, CheckMeshletVertexIndexBuffer) // Arrange const uint32_t vertexCount = 210; TestMeshVertex vertexList[vertexCount]; + uint32_t indexList[vertexCount / 3]; auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); // Act - auto result = ElemBuildMeshlets(vertexBuffer, NULL); + auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); // Assert ASSERT_FALSE(result.HasErrors); @@ -141,10 +160,12 @@ UTEST(MeshBuilder, CheckMeshletTriangleIndexBuffer) // Arrange const uint32_t vertexCount = 210; TestMeshVertex vertexList[vertexCount]; + uint32_t indexList[vertexCount / 3]; auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); // Act - auto result = ElemBuildMeshlets(vertexBuffer, NULL); + auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); // Assert ASSERT_FALSE(result.HasErrors); From 2ed20832ec40204548c927a7c9fb176504a31af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 27 Dec 2024 23:44:11 +0100 Subject: [PATCH 45/93] Refactor resources build --- .gitignore | 3 + .../06-HelloMesh/Data/RenderMesh.hlsl | 27 ++--- samples/Elemental/06-HelloMesh/main.c | 45 ++++--- .../SceneLoading/SceneLoaderGltf.cpp | 1 - .../SceneLoading/SceneLoaderObj.cpp | 20 +++- src/ElementalTools/ToolsUtils.cpp | 16 +-- utilities/cmake/AppPackaging.cmake | 111 ++++++++++++------ 7 files changed, 146 insertions(+), 77 deletions(-) diff --git a/.gitignore b/.gitignore index f7b8552c..ab427c02 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ *.ini *.gltf *.bin +*.tga +*.mtl xcuserdata/ packages/ @@ -37,3 +39,4 @@ bld/ external/shader-compilers/ .DS_Store artifacts/ +*.db diff --git a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl index d616489e..37a33c5f 100644 --- a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl @@ -1,9 +1,13 @@ struct ShaderParameters { - uint32_t VertexBufferIndex; - uint32_t MeshletBufferIndex; - uint32_t MeshletVertexIndexBufferIndex; - uint32_t MeshletTriangleIndexBufferIndex; + uint32_t MeshBuffer; + uint32_t VertexBufferOffset; + uint32_t MeshletOffset; + uint32_t MeshletVertexIndexOffset; + uint32_t MeshletTriangleIndexOffset; + uint32_t Reserved1; + uint32_t Reserved2; + uint32_t Reserved3; float4 RotationQuaternion; float Zoom; float AspectRatio; @@ -89,8 +93,8 @@ void MeshMain(in uint groupId: SV_GroupID, { uint meshletIndex = groupId; - ByteAddressBuffer meshletBuffer = ResourceDescriptorHeap[parameters.MeshletBufferIndex]; - ElemMeshlet meshlet = meshletBuffer.Load(meshletIndex * sizeof(ElemMeshlet)); + ByteAddressBuffer meshBuffer = ResourceDescriptorHeap[parameters.MeshBuffer]; + ElemMeshlet meshlet = meshBuffer.Load(parameters.MeshletOffset + meshletIndex * sizeof(ElemMeshlet)); SetMeshOutputCounts(meshlet.VertexIndexCount, meshlet.TriangleCount); @@ -106,11 +110,8 @@ void MeshMain(in uint groupId: SV_GroupID, float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); float4x4 inverseTransposeWorldMatrix = worldMatrix; - ByteAddressBuffer meshletVertexIndexBuffer = ResourceDescriptorHeap[parameters.MeshletVertexIndexBufferIndex]; - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - - uint vertexIndex = meshletVertexIndexBuffer.Load((meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); - Vertex vertex = vertexBuffer.Load(vertexIndex * sizeof(Vertex)); + uint vertexIndex = meshBuffer.Load(parameters.MeshletVertexIndexOffset + (meshlet.VertexIndexOffset + groupThreadId) * sizeof(uint)); + Vertex vertex = meshBuffer.Load(parameters.VertexBufferOffset + vertexIndex * sizeof(Vertex)); vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose @@ -119,9 +120,7 @@ void MeshMain(in uint groupId: SV_GroupID, if (groupThreadId < meshlet.TriangleCount) { - ByteAddressBuffer meshletTriangleIndexBuffer = ResourceDescriptorHeap[parameters.MeshletTriangleIndexBufferIndex]; - uint triangleIndex = meshletTriangleIndexBuffer.Load((meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); - + uint triangleIndex = meshBuffer.Load(parameters.MeshletTriangleIndexOffset + (meshlet.TriangleOffset + groupThreadId) * sizeof(uint)); indices[groupThreadId] = unpack_u8u32(triangleIndex).xyz; } } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index b99712a3..7a4f5042 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -6,15 +6,19 @@ #include "SampleSceneLoader.h" // TODO: Take all the code from the common headers and integrate it here +// SampleSceneLoaderNeeds to disappear - -/* typedef struct { - uint32_t VertexBuffer; - uint32_t MeshletBuffer; - uint32_t MeshletVertexIndexBuffer; - uint32_t MeshletTriangleIndexBuffer; + // TODO: Embed that into a buffer + uint32_t MeshBuffer; + uint32_t VertexBufferOffset; + uint32_t MeshletOffset; + uint32_t MeshletVertexIndexOffset; + uint32_t MeshletTriangleIndexOffset; + uint32_t Reserved1; + uint32_t Reserved2; + uint32_t Reserved3; SampleVector4 RotationQuaternion; float Zoom; float AspectRatio; @@ -41,6 +45,7 @@ typedef struct SampleInputsApplication InputsApplication; SampleInputsModelViewer InputsModelViewer; SampleSceneData TestSceneData; + SampleGpuMemory GpuMemory; // TODO: To remove } ApplicationPayload; void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); @@ -104,11 +109,13 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues // TODO: Having GPU Upload is still annoying 😞 - applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + //applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); + applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(256)); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - //LoadMesh(&applicationPayload->TestMeshData, "kitten.scene", applicationPayload); - //LoadMesh(&applicationPayload->TestMeshData, "buddha.scene", applicationPayload); + SampleLoadScene("kitten.scene", &applicationPayload->TestSceneData); + SampleLoadMeshData(&applicationPayload->TestSceneData.Meshes[0], &applicationPayload->GpuMemory); + //SampleLoadScene("buddha.scene", &applicationPayload->TestSceneData); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -148,7 +155,7 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); - //FreeMesh(&applicationPayload->TestMeshData); + SampleFreeScene(&applicationPayload->TestSceneData); ElemFreePipelineState(applicationPayload->GraphicsPipeline); ElemFreeSwapChain(applicationPayload->SwapChain); @@ -195,9 +202,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void if (SampleMagnitudeSquaredV3(modelViewerState->RotationDelta)) { - SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 1, 0, 0 }, modelViewerState->RotationDelta.X), - SampleMulQuat(SampleCreateQuaternion((SampleVector3){ 0, 0, 1 }, modelViewerState->RotationDelta.Z), - SampleCreateQuaternion((SampleVector3){ 0, 1, 0 }, modelViewerState->RotationDelta.Y))); + SampleVector4 rotationQuaternion = SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 1, 0, 0 }, modelViewerState->RotationDelta.X), + SampleMulQuat(SampleCreateQuaternion((ElemVector3){ 0, 0, 1 }, modelViewerState->RotationDelta.Z), + SampleCreateQuaternion((ElemVector3){ 0, 1, 0 }, modelViewerState->RotationDelta.Y))); applicationPayload->ShaderParameters.RotationQuaternion = SampleMulQuat(rotationQuaternion, applicationPayload->ShaderParameters.RotationQuaternion); } @@ -227,7 +234,13 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); - //ElemDispatchMesh(commandList, applicationPayload->TestMeshData.MeshletCount, 1, 1); + + SampleMeshPrimitiveHeader* meshPrimitive = &applicationPayload->TestSceneData.Meshes[0].MeshPrimitives[0]; + applicationPayload->ShaderParameters.VertexBufferOffset = meshPrimitive->VertexBufferOffset; + applicationPayload->ShaderParameters.MeshletOffset = meshPrimitive->MeshletOffset; + applicationPayload->ShaderParameters.MeshletVertexIndexOffset = meshPrimitive->MeshletVertexIndexOffset; + applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPrimitive->MeshletTriangleIndexOffset; + ElemDispatchMesh(commandList, meshPrimitive->MeshletCount, 1, 1); ElemEndRenderPass(commandList); @@ -261,8 +274,4 @@ int main(int argc, const char* argv[]) .FreeHandler = FreeSample, .Payload = &payload }); -}*/ - -int main(int argc, const char* argv[]) -{ } diff --git a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp index 554394ab..f1688457 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp @@ -232,7 +232,6 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* { auto sceneNode = &sceneNodes[i]; auto gltfNode = &data->nodes[i]; - printf("Test: %s\n", gltfNode->name); sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(gltfNode->name)).Pointer; sceneNode->Rotation = {}; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index 345d57fd..7e1f58b3 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -309,10 +309,26 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o ApplyObjBoundingBoxInverseTranslation(boundingBoxCenter, &meshPrimitive->BoundingBox); } - mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + if (objectData->name) + { + mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + } + else + { + mesh->Name = "Mesh"; + } + mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; - sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + if (objectData->name) + { + sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + } + else + { + sceneNode->Name = "Node"; + } + sceneNode->NodeType = ElemSceneNodeType_Mesh; sceneNode->ReferenceIndex = i; diff --git a/src/ElementalTools/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp index 8749b40c..e47f39d3 100644 --- a/src/ElementalTools/ToolsUtils.cpp +++ b/src/ElementalTools/ToolsUtils.cpp @@ -166,10 +166,10 @@ ElemToolsMatrix4x4 ElemToolsCreateScaleMatrix(float scale) { ElemToolsMatrix4x4 result; - result.Rows[0] = { scale, 0.0f, 0.0f, 0.0f }; - result.Rows[1] = { 0.0f, scale, 0.0f, 0.0f }; - result.Rows[2] = { 0.0f, 0.0f, scale, 0.0f }; - result.Rows[3] = { 0.0f, 0.0f, 0.0f, 1.0f }; + result.Rows[0] = {{ scale, 0.0f, 0.0f, 0.0f }}; + result.Rows[1] = {{ 0.0f, scale, 0.0f, 0.0f }}; + result.Rows[2] = {{ 0.0f, 0.0f, scale, 0.0f }}; + result.Rows[3] = {{ 0.0f, 0.0f, 0.0f, 1.0f }}; return result; } @@ -178,10 +178,10 @@ ElemToolsMatrix4x4 ElemToolsCreateTranslationMatrix(ElemToolsVector3 translation { ElemToolsMatrix4x4 result; - result.Rows[0] = { 1.0f, 0.0f, 0.0f, 0.0f }; - result.Rows[1] = { 0.0f, 1.0f, 0.0f, 0.0f }; - result.Rows[2] = { 0.0f, 0.0f, 1.0f, 0.0f }; - result.Rows[3] = { translation.X, translation.Y, translation.Z, 1.0f }; + result.Rows[0] = {{ 1.0f, 0.0f, 0.0f, 0.0f }}; + result.Rows[1] = {{ 0.0f, 1.0f, 0.0f, 0.0f }}; + result.Rows[2] = {{ 0.0f, 0.0f, 1.0f, 0.0f }}; + result.Rows[3] = {{ translation.X, translation.Y, translation.Z, 1.0f }}; return result; } diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index b7abb85f..1657314f 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -15,24 +15,40 @@ function(get_compiler_bin_path binary_name out_var) set(${out_var} "${bin_path}" PARENT_SCOPE) endfunction() -function(configure_resources_for_compiler target_name result_var name binary_name source_exts dest_ext) + +function(configure_resources_for_compiler + target_name # e.g. MyApp + result_var # e.g. compiled_files_for_X + name # e.g. HLSL + binary_name # e.g. ShaderCompiler + source_exts # ".hlsl" + dest_ext # ".shader" + resource_root # the folder from resource_list if it is a directory + ) # Convert commas to semicolons if present string(REPLACE "," ";" source_exts "${source_exts}") get_target_property(target_sources ${target_name} SOURCES) + set(resource_files "") foreach(src IN LISTS target_sources) + # Combine the "folder or file" from resource_root with the target source + # if your calling code is using it that way. (This part might vary depending on your usage.) set(abs_src "${CMAKE_CURRENT_SOURCE_DIR}/${src}") - if(IS_DIRECTORY ${abs_src}) + + # If we are dealing with a directory in resource_root or if 'src' *is* that directory, + # then GLOB_RECURSE subfiles + if(IS_DIRECTORY "${abs_src}") foreach(ext IN LISTS source_exts) file(GLOB_RECURSE matched_files "${abs_src}/*${ext}") list(APPEND resource_files ${matched_files}) endforeach() else() - get_filename_component(this_ext ${abs_src} EXT) + # It's a single file—check extension + get_filename_component(this_ext "${abs_src}" EXT) foreach(ext IN LISTS source_exts) if("${this_ext}" STREQUAL "${ext}") - list(APPEND resource_files ${abs_src}) + list(APPEND resource_files "${abs_src}") break() endif() endforeach() @@ -44,9 +60,18 @@ function(configure_resources_for_compiler target_name result_var name binary_nam return() endif() + # Use the new param as the "root" for the relative subfolder structure + # If resource_root is empty or not a directory, you might choose a fallback: + if(NOT IS_DIRECTORY "${resource_root}") + # fallback if needed + set(resource_root "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + # Get path to the compiler binary get_compiler_bin_path(${binary_name} compiler_bin) - # ARGN holds default parameters passed from the main function + # Grab any remaining ARGN as default parameters + # (If you’re using ARGN for something else, adapt accordingly.) set(args_to_parse ${ARGN}) set(default_params "") foreach(param IN LISTS args_to_parse) @@ -58,49 +83,53 @@ function(configure_resources_for_compiler target_name result_var name binary_nam set(compiled_files "") foreach(file IN LISTS resource_files) - get_filename_component(file_path ${file} ABSOLUTE) - get_filename_component(file_dir ${file_path} DIRECTORY) - get_filename_component(file_name ${file_path} NAME_WE) + get_filename_component(file_path "${file}" ABSOLUTE) + get_filename_component(file_dir "${file_path}" DIRECTORY) + get_filename_component(file_name "${file_path}" NAME_WE) - file(RELATIVE_PATH relative_dir ${CMAKE_CURRENT_SOURCE_DIR} ${file_dir}) + # Compute the relative subfolder structure from 'resource_root' + file(RELATIVE_PATH relative_dir "${resource_root}" "${file_dir}") + + # Reproduce that structure inside the build folder set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/${relative_dir}") - file(MAKE_DIRECTORY ${output_dir}) + file(MAKE_DIRECTORY "${output_dir}") set(output_file "${output_dir}/${file_name}${dest_ext}") add_custom_command( - OUTPUT ${output_file} - COMMAND ${compiler_bin} ${default_params} ${file} ${output_file} - DEPENDS ${file} + OUTPUT "${output_file}" + COMMAND "${compiler_bin}" ${default_params} "${file}" "${output_file}" + DEPENDS "${file}" COMMENT "Compiling ${name}: ${file}" - WORKING_DIRECTORY ${file_dir} + WORKING_DIRECTORY "${file_dir}" ) - set_source_files_properties(${output_file} PROPERTIES GENERATED TRUE) - target_sources(${target_name} PRIVATE ${output_file}) - list(APPEND compiled_files ${output_file}) + set_source_files_properties("${output_file}" PROPERTIES GENERATED TRUE) + target_sources("${target_name}" PRIVATE "${output_file}") + list(APPEND compiled_files "${output_file}") # Extra Vulkan variant for HLSL on Windows if("${name}" STREQUAL "HLSL" AND WIN32) set(vulkan_output_file "${output_dir}/${file_name}_vulkan${dest_ext}") add_custom_command( - OUTPUT ${vulkan_output_file} - COMMAND ${compiler_bin} ${default_params} --target-api vulkan ${file} ${vulkan_output_file} - DEPENDS ${file} + OUTPUT "${vulkan_output_file}" + COMMAND "${compiler_bin}" ${default_params} --target-api vulkan "${file}" "${vulkan_output_file}" + DEPENDS "${file}" COMMENT "Compiling ${name} (Vulkan): ${file}" - WORKING_DIRECTORY ${file_dir} + WORKING_DIRECTORY "${file_dir}" ) - set_source_files_properties(${vulkan_output_file} PROPERTIES GENERATED TRUE) - target_sources(${target_name} PRIVATE ${vulkan_output_file}) - list(APPEND compiled_files ${vulkan_output_file}) + set_source_files_properties("${vulkan_output_file}" PROPERTIES GENERATED TRUE) + target_sources("${target_name}" PRIVATE "${vulkan_output_file}") + list(APPEND compiled_files "${vulkan_output_file}") endif() endforeach() set(${result_var} "${compiled_files}" PARENT_SCOPE) endfunction() + function(configure_resource_compilation target_name resource_list) - # Reintroduce original logic for iOS and Debug + # iOS, debug logic, etc. if(BUILD_FOR_IOS) set(SHADER_COMPILER_DEFAULT_OPTIONS "--target-platform iOS") else() @@ -109,7 +138,6 @@ function(configure_resource_compilation target_name resource_list) endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") - # Append debug flag to shader options if(NOT "${SHADER_COMPILER_DEFAULT_OPTIONS}" STREQUAL "") set(SHADER_COMPILER_DEFAULT_OPTIONS "${SHADER_COMPILER_DEFAULT_OPTIONS} --debug") else() @@ -119,8 +147,7 @@ function(configure_resource_compilation target_name resource_list) set(MESH_COMPILER_DEFAULT_OPTIONS "") - # Use '|' as delimiters to avoid semicolon issues - # Format: Name|BinaryName|SourceExtensions|DestExtension|DefaultParams... + # Use '|' as delimiters set(COMPILERS_LIST "HLSL|ShaderCompiler|.hlsl|.shader|${SHADER_COMPILER_DEFAULT_OPTIONS}" "MESH|SceneCompiler|.obj|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" @@ -129,7 +156,6 @@ function(configure_resource_compilation target_name resource_list) set(all_compiled_resources "") foreach(compiler_entry IN LISTS COMPILERS_LIST) - # Convert '|' to ';' to parse fields string(REPLACE "|" ";" fields_string "${compiler_entry}") set(fields ${fields_string}) list(LENGTH fields fields_length) @@ -148,13 +174,22 @@ function(configure_resource_compilation target_name resource_list) set(default_params "") endif() + #----------------------------------------------------------------- + # Here we call our new configure_resources_for_compiler. + # The last param is the folder from resource_list if you want + # to treat it as the "root" for preserving subfolders. + # + # You could pass `resource_list` directly. If your `resource_list` + # has multiple entries, you might loop them or parse them individually. + #----------------------------------------------------------------- configure_resources_for_compiler( - ${target_name} + "${target_name}" compiled_files_for_${name} - ${name} - ${binary_name} + "${name}" + "${binary_name}" "${source_exts}" - ${dest_ext} + "${dest_ext}" + "${resource_list}" # TODO: We can remove that parameter ${default_params} ) @@ -264,7 +299,15 @@ function(configure_project_package target_name install_folder) foreach(file IN LISTS ARG_RESOURCES) get_filename_component(file_name "${file}" NAME) get_filename_component(full_path "${file}" ABSOLUTE) - set(output_file "${output_folder}/Data/${file_name}") + get_filename_component(file_dir "${full_path}" DIRECTORY) + + set(resource_root "${CMAKE_CURRENT_BINARY_DIR}/Data") + + file(RELATIVE_PATH relative_dir "${resource_root}" "${file_dir}") + set(output_subdir "${output_folder}/Data/${relative_dir}") + file(MAKE_DIRECTORY "${output_subdir}") + + set(output_file "${output_subdir}/${file_name}") add_custom_command( OUTPUT "${output_file}" From a45a81ff74a04cb9da7784b7f1190cba0a765a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 28 Dec 2024 12:18:02 +0100 Subject: [PATCH 46/93] Update metal-cpp version --- external/metal-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/metal-cpp b/external/metal-cpp index 89f87c73..5caea74c 160000 --- a/external/metal-cpp +++ b/external/metal-cpp @@ -1 +1 @@ -Subproject commit 89f87c73755940fb2019f874df5a8ffff81bad29 +Subproject commit 5caea74c5f77492add32b7cad109d796e342ab49 From f7aca1f7154cc596a01525d54d6d7ba19b469350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 29 Dec 2024 10:21:41 +0100 Subject: [PATCH 47/93] Start working on texture compiler. For now, texture loading and mipmaps generation. --- .gitmodules | 3 + external/CMakeLists.txt | 12 ++- external/stb | 1 + samples/Elemental/99-Renderer/main.c | 2 +- .../ElementalTools/02-SceneCompiler/main.c | 8 -- .../03-TextureCompiler/CMakeLists.txt | 9 +++ .../ElementalTools/03-TextureCompiler/main.c | 77 +++++++++++++++++++ src/ElementalTools/ElementalTools.h | 62 +++++++++++++++ src/ElementalTools/ElementalToolsLoader.c | 66 ++++++++++++++++ .../Platforms/Apple/PreCompiledHeader.h | 2 + .../Platforms/Linux/PreCompiledHeader.h | 2 + .../Platforms/Microsoft/PreCompiledHeader.h | 2 + .../SceneLoading/SceneLoaderObj.cpp | 11 ++- src/ElementalTools/Textures/TextureLoader.cpp | 67 ++++++++++++++++ src/ElementalTools/Textures/TextureLoader.h | 5 ++ .../Textures/TextureLoaderStb.cpp | 52 +++++++++++++ .../Textures/TextureProcessing.cpp | 69 +++++++++++++++++ src/ElementalTools/UnityBuild.cpp | 13 +++- utilities/cmake/AppPackaging.cmake | 2 + 19 files changed, 448 insertions(+), 17 deletions(-) create mode 160000 external/stb create mode 100644 samples/ElementalTools/03-TextureCompiler/CMakeLists.txt create mode 100644 samples/ElementalTools/03-TextureCompiler/main.c create mode 100644 src/ElementalTools/Textures/TextureLoader.cpp create mode 100644 src/ElementalTools/Textures/TextureLoader.h create mode 100644 src/ElementalTools/Textures/TextureLoaderStb.cpp create mode 100644 src/ElementalTools/Textures/TextureProcessing.cpp diff --git a/.gitmodules b/.gitmodules index db47cd7e..89b6ce20 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "external/cgltf"] path = external/cgltf url = https://github.com/jkuhlmann/cgltf.git +[submodule "external/stb"] + path = external/stb + url = https://github.com/nothings/stb.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index dfac4dc8..1d2c29b1 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,3 +1,5 @@ +# TODO: Make a folder per bin external dep + #======================================================================= # DirectX Shader Compiler #======================================================================= @@ -225,11 +227,19 @@ if(NOT BUILD_FOR_IOS) target_include_directories(cgltf INTERFACE ./cgltf/) endif() +#======================================================================= +# stb +#======================================================================= +if(NOT BUILD_FOR_IOS) + add_library(stb INTERFACE) + target_include_directories(stb INTERFACE ./stb/) +endif() + #======================================================================= # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf stb) #======================================================================= # xxHash diff --git a/external/stb b/external/stb new file mode 160000 index 00000000..5c205738 --- /dev/null +++ b/external/stb @@ -0,0 +1 @@ +Subproject commit 5c205738c191bcb0abc65c4febfa9bd25ff35234 diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index f5e1d7fa..21e2c0c8 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -295,7 +295,7 @@ int main(int argc, const char* argv[]) ApplicationPayload payload = { .AppSettings = SampleParseAppSettings(argc, argv), - .ScenePath = "sponza.scene" + .ScenePath = "Sponza/sponza.scene" }; int32_t scenePathIndex = argc - 1; diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 63fa6f0a..112e65a3 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -178,14 +178,6 @@ int main(int argc, const char* argv[]) SampleInitTimer(); double initialTimer = SampleGetTimerValueInMS(); - ElemToolsDataSpan inputData = SampleReadFile(inputPath, false); - - if (inputData.Length == 0) - { - printf("File doesn't exist.\n"); - return 1; - } - ElemLoadSceneOptions loadSceneOptions = {}; // HACK: For now we hardcode options diff --git a/samples/ElementalTools/03-TextureCompiler/CMakeLists.txt b/samples/ElementalTools/03-TextureCompiler/CMakeLists.txt new file mode 100644 index 00000000..69c2c34f --- /dev/null +++ b/samples/ElementalTools/03-TextureCompiler/CMakeLists.txt @@ -0,0 +1,9 @@ +if(NOT BUILD_FOR_IOS) + set(SAMPLE_NAME TextureCompiler) + + add_executable(${SAMPLE_NAME} main.c) + target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) + target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalToolsInterface) + + configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES ElementalTools) +endif() diff --git a/samples/ElementalTools/03-TextureCompiler/main.c b/samples/ElementalTools/03-TextureCompiler/main.c new file mode 100644 index 00000000..43a30f24 --- /dev/null +++ b/samples/ElementalTools/03-TextureCompiler/main.c @@ -0,0 +1,77 @@ +#include "ElementalTools.h" +#include "SampleUtils.h" + +int main(int argc, const char* argv[]) +{ + // TODO: Refactor options parsing and put it in a header common + // to tools sample and normal samples + if (argc < 3) + { + printf("USAGE: TextureCompiler [options] inputfile outputfile\n"); + printf("\n"); + printf("OPTIONS:\n"); + printf("\n"); + return 0; + } + + int32_t inputPathIndex = argc - 2; + const char* inputPath = argv[inputPathIndex]; + + int32_t outputPathIndex = argc - 1; + const char* outputPath = argv[outputPathIndex]; + + // TODO: Add more checks + for (uint32_t i = 1; i < (uint32_t)(argc - 2); i++) + { + printf("Options: %s\n", argv[i]); + } + + printf("Compiling texture: %s\n", inputPath); + + SampleInitTimer(); + double initialTimer = SampleGetTimerValueInMS(); + + ElemLoadTextureResult loadTextureResult = ElemLoadTexture(inputPath, NULL); + + DisplayOutputMessages("LoadTexture", loadTextureResult.Messages); + + if (loadTextureResult.HasErrors) + { + return 1; + } + + printf("Loaded texture in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); + + ElemTextureMipDataSpan mipData = loadTextureResult.MipData; + + if (mipData.Length == 1) + { + double beforeMipGenerationTimer = SampleGetTimerValueInMS(); + printf("Generating MipData...\n"); + + ElemGenerateTextureMipDataResult generateMipDataResult = ElemGenerateTextureMipData(loadTextureResult.Format, loadTextureResult.MipData.Items[0], NULL); + + printf("GeneratedMips Count: %d\n", generateMipDataResult.MipData.Length); + + + mipData = generateMipDataResult.MipData; + printf("Generated MipData in %.2fs\n", (SampleGetTimerValueInMS() - beforeMipGenerationTimer) / 1000.0); + } + + double beforeEncodeTimer = SampleGetTimerValueInMS(); + printf("Encoding data...\n"); + + for (uint32_t i = 0; i < mipData.Length; i++) + { + ElemTextureMipData* mipLevelData = &mipData.Items[i]; + printf("Size: %dx%d\n", mipLevelData->Width, mipLevelData->Height); + } + + printf("Encoded data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); + + printf("Writing texture data to: %s\n", outputPath); + + FILE* file = fopen(outputPath, "wb"); + fclose(file); + printf("Texture compiled in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); +} diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index c19009aa..ecc9b358 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -352,6 +352,7 @@ ElemToolsAPI ElemLoadSceneResult ElemLoadScene(const char* path, const ElemLoadS typedef struct { // TODO: Allow bypass mesh format + // TODO: Allow customize index packing uint32_t Reserved; } ElemBuildMeshletsOptions; @@ -386,6 +387,67 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf // TODO: Do an optimize mesh function that can be used to optimize a normal mesh (for Raytracing for example) +//------------------------------------------------------------------------ +// Module: Textures +//------------------------------------------------------------------------ + +typedef enum +{ + ElemToolsGraphicsFormat_R8G8B8A8_SRGB +} ElemToolsGraphicsFormat; + +typedef enum +{ + ElemLoadTextureFileFormat_Unknown = 0, + ElemLoadTextureFileFormat_Tga = 1, +} ElemLoadTextureFileFormat; + +typedef struct +{ + uint32_t Width; + uint32_t Height; + ElemToolsDataSpan Data; +} ElemTextureMipData; + +typedef struct +{ + ElemTextureMipData* Items; + uint32_t Length; +} ElemTextureMipDataSpan; + +typedef struct +{ + uint32_t Reserved; +} ElemLoadTextureOptions; + +typedef struct +{ + ElemLoadTextureFileFormat FileFormat; + ElemToolsGraphicsFormat Format; + uint32_t Width; + uint32_t Height; + ElemTextureMipDataSpan MipData; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemLoadTextureResult; + +typedef struct +{ + // TODO: Alpha options + uint32_t Reserved; +} ElemGenerateTextureMipDataOptions; + +typedef struct +{ + ElemTextureMipDataSpan MipData; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemGenerateTextureMipDataResult; + +ElemToolsAPI ElemLoadTextureResult ElemLoadTexture(const char* path, const ElemLoadTextureOptions* options); + +ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, const ElemGenerateTextureMipDataOptions* options); + #ifdef UseToolsLoader #ifndef ElementalToolsLoader #include "ElementalToolsLoader.c" diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index e5030190..30076ec3 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -28,6 +28,8 @@ typedef struct ElementalToolsFunctions ElemShaderCompilationResult (*ElemCompileShaderLibrary)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *); ElemLoadSceneResult (*ElemLoadScene)(char const *, ElemLoadSceneOptions const *); ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *); + ElemLoadTextureResult (*ElemLoadTexture)(char const *, ElemLoadTextureOptions const *); + ElemGenerateTextureMipDataResult (*ElemGenerateTextureMipData)(ElemToolsGraphicsFormat, ElemTextureMipData, ElemGenerateTextureMipDataOptions const *); } ElementalToolsFunctions; @@ -85,6 +87,8 @@ static bool LoadElementalToolsFunctionPointers(void) listElementalToolsFunctions.ElemCompileShaderLibrary = (ElemShaderCompilationResult (*)(ElemToolsGraphicsApi, ElemToolsPlatform, char const *, ElemCompileShaderOptions const *))GetElementalToolsFunctionPointer("ElemCompileShaderLibrary"); listElementalToolsFunctions.ElemLoadScene = (ElemLoadSceneResult (*)(char const *, ElemLoadSceneOptions const *))GetElementalToolsFunctionPointer("ElemLoadScene"); listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); + listElementalToolsFunctions.ElemLoadTexture = (ElemLoadTextureResult (*)(char const *, ElemLoadTextureOptions const *))GetElementalToolsFunctionPointer("ElemLoadTexture"); + listElementalToolsFunctions.ElemGenerateTextureMipData = (ElemGenerateTextureMipDataResult (*)(ElemToolsGraphicsFormat, ElemTextureMipData, ElemGenerateTextureMipDataOptions const *))GetElementalToolsFunctionPointer("ElemGenerateTextureMipData"); functionPointersLoadedElementalTools = 1; @@ -231,3 +235,65 @@ static inline ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBu return listElementalToolsFunctions.ElemBuildMeshlets(vertexBuffer, indexBuffer, options); } + +static inline ElemLoadTextureResult ElemLoadTexture(char const * path, ElemLoadTextureOptions const * options) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + + #ifdef __cplusplus + ElemLoadTextureResult result = {}; + #else + ElemLoadTextureResult result = (ElemLoadTextureResult){0}; + #endif + + return result; + } + + if (!listElementalToolsFunctions.ElemLoadTexture) + { + assert(listElementalToolsFunctions.ElemLoadTexture); + + #ifdef __cplusplus + ElemLoadTextureResult result = {}; + #else + ElemLoadTextureResult result = (ElemLoadTextureResult){0}; + #endif + + return result; + } + + return listElementalToolsFunctions.ElemLoadTexture(path, options); +} + +static inline ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, ElemGenerateTextureMipDataOptions const * options) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + + #ifdef __cplusplus + ElemGenerateTextureMipDataResult result = {}; + #else + ElemGenerateTextureMipDataResult result = (ElemGenerateTextureMipDataResult){0}; + #endif + + return result; + } + + if (!listElementalToolsFunctions.ElemGenerateTextureMipData) + { + assert(listElementalToolsFunctions.ElemGenerateTextureMipData); + + #ifdef __cplusplus + ElemGenerateTextureMipDataResult result = {}; + #else + ElemGenerateTextureMipDataResult result = (ElemGenerateTextureMipDataResult){0}; + #endif + + return result; + } + + return listElementalToolsFunctions.ElemGenerateTextureMipData(format, baseMip, options); +} diff --git a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h index 75357e68..e092c362 100644 --- a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h @@ -39,6 +39,8 @@ #include "meshoptimizer.h" #include "fast_obj.h" #include "cgltf.h" +#include "stb_image.h" +#include "stb_image_resize2.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h index 0a9c4d60..21f58a65 100644 --- a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h @@ -39,6 +39,8 @@ #include "meshoptimizer.h" #include "fast_obj.h" #include "cgltf.h" +#include "stb_image.h" +#include "stb_image_resize2.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h index cbfa4fe5..791271b8 100644 --- a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h @@ -39,6 +39,8 @@ using namespace Microsoft::WRL; #include "meshoptimizer.h" #include "fast_obj.h" #include "cgltf.h" +#include "stb_image.h" +#include "stb_image_resize2.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index 7e1f58b3..cb5911ea 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -201,6 +201,11 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); auto meshPrimitiveInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); + for (uint32_t i = 0; i < objFileData->material_count; i++) + { + printf("Material: %s\n", objFileData->materials[i].name); + } + for (uint32_t i = 0; i < objFileData->object_count; i++) { auto mesh = &meshes[i]; @@ -213,8 +218,6 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o continue; } - printf("Object/Mesh: %s\n", objectData->name); - auto currentFaceStart = objectData->face_offset; auto currentFaceEnd = objectData->face_offset + objectData->face_count; @@ -226,8 +229,6 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o } auto currentMaterial = objFileData->face_materials[currentFaceStart]; - printf(" MeshPrimitive (Material: %d)\n", currentMaterial); - auto meshPrimitiveCount = 0u; auto meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; @@ -259,8 +260,6 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o { currentMaterial = faceMaterial; - printf(" MeshPrimitive (Material: %d)\n", currentMaterial); - auto meshPrimitiveIndexOffset = indexOffset + meshPrimitiveInfo->IndexCount; meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; diff --git a/src/ElementalTools/Textures/TextureLoader.cpp b/src/ElementalTools/Textures/TextureLoader.cpp new file mode 100644 index 00000000..67eb9b18 --- /dev/null +++ b/src/ElementalTools/Textures/TextureLoader.cpp @@ -0,0 +1,67 @@ +#include "TextureLoader.h" +#include "TextureLoaderStb.cpp" + +// TODO: Do one for each thread +static MemoryArena TextureLoaderMemoryArena; + +void InitTextureLoaderMemoryArena() +{ + if (TextureLoaderMemoryArena.Storage == nullptr) + { + TextureLoaderMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + } + + SystemClearMemoryArena(TextureLoaderMemoryArena); +} + +MemoryArena GetTextureLoaderMemoryArena() +{ + return TextureLoaderMemoryArena; +} + +ElemLoadTextureFileFormat GetLoadTextureFileFormatFromPath(const char* path) +{ + auto pathSpan = ReadOnlySpan(path); + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto lastIndex = SystemLastIndexOf(path, '.'); + SystemAssert(lastIndex != -1); + + auto extension = SystemPushArray(stackMemoryArena, pathSpan.Length - lastIndex); + SystemCopyBuffer(extension, pathSpan.Slice(lastIndex + 1)); + + if (SystemFindSubString(extension, "tga") != -1) + { + return ElemLoadTextureFileFormat_Tga; + } + + return ElemLoadTextureFileFormat_Unknown; +} + +ElemToolsAPI ElemLoadTextureResult ElemLoadTexture(const char* path, const ElemLoadTextureOptions* options) +{ + InitTextureLoaderMemoryArena(); + + ElemLoadTextureOptions loadTextureOptions = {}; + + if (options) + { + loadTextureOptions = *options; + } + + auto textureFileFormat = GetLoadTextureFileFormatFromPath(path); + + // TODO: Refactor that with array entries and function pointer + switch (textureFileFormat) + { + case ElemLoadTextureFileFormat_Tga: + return LoadStbTexture(path, textureFileFormat, &loadTextureOptions); + + default: + return + { + .Messages = ConstructErrorMessageSpan(GetTextureLoaderMemoryArena(), "Texture format is not supported."), + .HasErrors = true + }; + }; +} diff --git a/src/ElementalTools/Textures/TextureLoader.h b/src/ElementalTools/Textures/TextureLoader.h new file mode 100644 index 00000000..77e30f05 --- /dev/null +++ b/src/ElementalTools/Textures/TextureLoader.h @@ -0,0 +1,5 @@ +#pragma once + +#include "SystemMemory.h" + +MemoryArena GetTextureLoaderMemoryArena(); diff --git a/src/ElementalTools/Textures/TextureLoaderStb.cpp b/src/ElementalTools/Textures/TextureLoaderStb.cpp new file mode 100644 index 00000000..79d9b9ad --- /dev/null +++ b/src/ElementalTools/Textures/TextureLoaderStb.cpp @@ -0,0 +1,52 @@ +#include "TextureLoader.h" +#include "ToolsUtils.h" +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + +ElemLoadTextureResult LoadStbTexture(const char* path, ElemLoadTextureFileFormat fileFormat, const ElemLoadTextureOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto textureLoaderMemoryArena = GetTextureLoaderMemoryArena(); + + auto messages = SystemPushArray(textureLoaderMemoryArena, 1024); + auto messageCount = 0u; + auto hasErrors = false; + + auto objFileData = LoadFileData(path); + + if (objFileData.Length == 0) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Error while reading texture file."), .HasErrors = true }; + } + + int32_t width, height, channels; + auto stbImageData = stbi_load_from_memory(objFileData.Pointer, objFileData.Length, &width, &height, &channels, STBI_rgb_alpha); + + if (!stbImageData) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Error while loading texture file."), .HasErrors = true }; + } + + auto imageDataLength = width * height * STBI_rgb_alpha; + auto imageData = SystemDuplicateBuffer(textureLoaderMemoryArena, ReadOnlySpan(stbImageData, imageDataLength)); + + auto mipData = SystemPushStruct(textureLoaderMemoryArena); + mipData->Width = width; + mipData->Height = height; + mipData->Data = { .Items = imageData.Pointer, .Length = (uint32_t)imageData.Length }, + + stbi_image_free(stbImageData); + ResetLoadFileDataMemory(); + + return + { + .FileFormat = fileFormat, + .Format = ElemToolsGraphicsFormat_R8G8B8A8_SRGB, + .Width = (uint32_t)width, + .Height = (uint32_t)height, + .MipData = { .Items = mipData, .Length = 1 }, + .Messages = { .Items = messages.Pointer, .Length = messageCount }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/Textures/TextureProcessing.cpp b/src/ElementalTools/Textures/TextureProcessing.cpp new file mode 100644 index 00000000..06bbc53d --- /dev/null +++ b/src/ElementalTools/Textures/TextureProcessing.cpp @@ -0,0 +1,69 @@ +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + +// TODO: Do one for each thread +static MemoryArena generateMipDataMemoryArena; + +void InitgenerateMipDataMemoryArena() +{ + if (generateMipDataMemoryArena.Storage == nullptr) + { + generateMipDataMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + } + + SystemClearMemoryArena(generateMipDataMemoryArena); +} + +ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, const ElemGenerateTextureMipDataOptions* options) +{ + InitgenerateMipDataMemoryArena(); + + auto messages = SystemPushArray(generateMipDataMemoryArena, 1024); + auto messageCount = 0u; + auto hasErrors = false; + + ElemGenerateTextureMipDataOptions generateMipDataOptions = {}; + + if (options) + { + generateMipDataOptions = *options; + } + + auto pixelSize = 4; // TODO: Check format first + auto maxDimension = SystemMax(baseMip.Width, baseMip.Height); + auto mipCount = (uint32_t)floorf(log2f((float)maxDimension)) + 1; + + auto mipData = SystemPushArray(generateMipDataMemoryArena, mipCount); + auto baseMipCopy = SystemDuplicateBuffer(generateMipDataMemoryArena, ReadOnlySpan(baseMip.Data.Items, baseMip.Data.Length)); + + mipData[0] = + { + .Width = baseMip.Width, + .Height = baseMip.Height, + .Data = { .Items = baseMipCopy.Pointer, .Length = (uint32_t)baseMipCopy.Length } + }; + + for (uint32_t i = 1; i < mipCount; i++) + { + auto mipLevelData = &mipData[i]; + + mipLevelData->Width = SystemMax(1u, baseMip.Width >> i); + mipLevelData->Height = SystemMax(1u, baseMip.Height >> i); + + auto mipLevelPixels = SystemPushArray(generateMipDataMemoryArena, mipLevelData->Width * mipLevelData->Height * pixelSize); + + stbir_resize_uint8_srgb(baseMip.Data.Items, baseMip.Width, baseMip.Height, 0, + mipLevelPixels.Pointer, mipLevelData->Width, mipLevelData->Height, 0, + STBIR_RGBA); + + mipLevelData->Data = { .Items = mipLevelPixels.Pointer, .Length = (uint32_t)mipLevelPixels.Length }; + } + + return + { + .MipData = { .Items = mipData.Pointer, .Length = (uint32_t)mipData.Length }, + .Messages = { .Items = messages.Pointer, .Length = messageCount }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp index 438bc538..513e5743 100644 --- a/src/ElementalTools/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -9,12 +9,23 @@ #define CGLTF_IMPLEMENTATION #include "cgltf.h" - #include "fast_obj.c" #include "SceneLoading/SceneLoader.cpp" #include "Meshes/MeshletBuilder.cpp" +#ifdef _WIN32 +#define STBI_WINDOWS_UTF8 +#endif + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" +#define STB_IMAGE_RESIZE_IMPLEMENTATION +#include "stb_image_resize2.h" + +#include "Textures/TextureLoader.cpp" +#include "Textures/TextureProcessing.cpp" + #include "SystemFunctions.cpp" #include "SystemDictionary.cpp" #include "SystemMemory.cpp" diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index 1657314f..4ca65814 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -146,12 +146,14 @@ function(configure_resource_compilation target_name resource_list) endif() set(MESH_COMPILER_DEFAULT_OPTIONS "") + set(TEXTURE_COMPILER_DEFAULT_OPTIONS "") # Use '|' as delimiters set(COMPILERS_LIST "HLSL|ShaderCompiler|.hlsl|.shader|${SHADER_COMPILER_DEFAULT_OPTIONS}" "MESH|SceneCompiler|.obj|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" "MESH|SceneCompiler|.gltf|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" + "TEXTURE|TextureCompiler|.tga|.texture|${TEXTURE_COMPILER_DEFAULT_OPTIONS}" ) set(all_compiled_resources "") From 26fde05fac383149e1231c47fca162f03b3a990a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 30 Dec 2024 08:05:40 +0100 Subject: [PATCH 48/93] Basic texture compiler working with BC7 --- .gitmodules | 3 + external/CMakeLists.txt | 15 ++- external/bc7enc_rdo | 1 + samples/Common/SampleTexture.h | 24 ++++ samples/Elemental/99-Renderer/main.c | 2 +- .../ElementalTools/03-TextureCompiler/main.c | 52 +++++++- src/ElementalTools/ElementalTools.h | 20 +++- src/ElementalTools/ElementalToolsLoader.c | 39 +++++- .../Platforms/Microsoft/PreCompiledHeader.h | 1 + .../Textures/TextureLoaderStb.cpp | 2 +- .../Textures/TextureProcessing.cpp | 112 ++++++++++++++++-- src/ElementalTools/UnityBuild.cpp | 2 + 12 files changed, 248 insertions(+), 25 deletions(-) create mode 160000 external/bc7enc_rdo create mode 100644 samples/Common/SampleTexture.h diff --git a/.gitmodules b/.gitmodules index 89b6ce20..9afba37b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "external/stb"] path = external/stb url = https://github.com/nothings/stb.git +[submodule "external/bc7enc_rdo"] + path = external/bc7enc_rdo + url = https://github.com/richgel999/bc7enc_rdo.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 1d2c29b1..e817d78b 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -235,11 +235,24 @@ if(NOT BUILD_FOR_IOS) target_include_directories(stb INTERFACE ./stb/) endif() +#======================================================================= +# bc7encoder_rdo +#======================================================================= +if(NOT BUILD_FOR_IOS) + set(SUPPORT_BC7E FALSE) # TODO: Needs to set it to true for GPU accell and ISPC + if(WIN32) + set(MSVC FALSE) + endif() + #add_subdirectory(./bc7enc_rdo) + add_library(bc7enc INTERFACE) + target_include_directories(bc7enc INTERFACE ./bc7enc_rdo/) +endif() + #======================================================================= # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf stb) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf stb bc7enc) #======================================================================= # xxHash diff --git a/external/bc7enc_rdo b/external/bc7enc_rdo new file mode 160000 index 00000000..e6990bc1 --- /dev/null +++ b/external/bc7enc_rdo @@ -0,0 +1 @@ +Subproject commit e6990bc11829c072d9f9e37296f3335072aab4e4 diff --git a/samples/Common/SampleTexture.h b/samples/Common/SampleTexture.h new file mode 100644 index 00000000..e9ec7d0b --- /dev/null +++ b/samples/Common/SampleTexture.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +typedef enum +{ + SampleTextureFormat_Unknown = 0, + SampleTextureFormat_BC7 = 1 +} SampleTextureFormat; + +typedef struct +{ + char FileId[7]; + SampleTextureFormat Format; + uint32_t Width; + uint32_t Height; + uint32_t MipCount; +} SampleTextureHeader; + +typedef struct +{ + uint32_t Offset; + uint32_t SizeInBytes; +} SampleTextureDataBlockEntry; diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 21e2c0c8..d045659c 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -295,7 +295,7 @@ int main(int argc, const char* argv[]) ApplicationPayload payload = { .AppSettings = SampleParseAppSettings(argc, argv), - .ScenePath = "Sponza/sponza.scene" + .ScenePath = "sponza/sponza.scene" }; int32_t scenePathIndex = argc - 1; diff --git a/samples/ElementalTools/03-TextureCompiler/main.c b/samples/ElementalTools/03-TextureCompiler/main.c index 43a30f24..a088b94f 100644 --- a/samples/ElementalTools/03-TextureCompiler/main.c +++ b/samples/ElementalTools/03-TextureCompiler/main.c @@ -1,5 +1,6 @@ #include "ElementalTools.h" #include "SampleUtils.h" +#include "SampleTexture.h" int main(int argc, const char* argv[]) { @@ -49,29 +50,68 @@ int main(int argc, const char* argv[]) double beforeMipGenerationTimer = SampleGetTimerValueInMS(); printf("Generating MipData...\n"); - ElemGenerateTextureMipDataResult generateMipDataResult = ElemGenerateTextureMipData(loadTextureResult.Format, loadTextureResult.MipData.Items[0], NULL); + ElemGenerateTextureMipDataResult generateMipDataResult = ElemGenerateTextureMipData(loadTextureResult.Format, &loadTextureResult.MipData.Items[0], NULL); - printf("GeneratedMips Count: %d\n", generateMipDataResult.MipData.Length); + DisplayOutputMessages("GenerateTextureMipData", generateMipDataResult.Messages); + + if (generateMipDataResult.HasErrors) + { + return 1; + } + printf("GeneratedMips Count: %d\n", generateMipDataResult.MipData.Length); mipData = generateMipDataResult.MipData; printf("Generated MipData in %.2fs\n", (SampleGetTimerValueInMS() - beforeMipGenerationTimer) / 1000.0); } + + printf("Writing texture data to: %s\n", outputPath); + FILE* file = fopen(outputPath, "wb"); + + SampleTextureHeader textureHeader = + { + .FileId = { 'T', 'E', 'X', 'T', 'U', 'R', 'E' }, + .Format = SampleTextureFormat_BC7, // TODO: Don't hardcode this + .Width = loadTextureResult.Width, + .Height = loadTextureResult.Height, + .MipCount = mipData.Length + }; + + fwrite(&textureHeader, sizeof(SampleTextureHeader), 1, file); + + // TODO: Get rid of malloc? + SampleTextureDataBlockEntry* mipDataOffsets = (SampleTextureDataBlockEntry*)malloc(sizeof(SampleTextureDataBlockEntry) * mipData.Length); + fwrite(mipDataOffsets, sizeof(SampleTextureDataBlockEntry), mipData.Length, file); double beforeEncodeTimer = SampleGetTimerValueInMS(); - printf("Encoding data...\n"); + // TODO: Add format in the logging message + printf("Compressing data...\n"); for (uint32_t i = 0; i < mipData.Length; i++) { ElemTextureMipData* mipLevelData = &mipData.Items[i]; printf("Size: %dx%d\n", mipLevelData->Width, mipLevelData->Height); + + ElemCompressTextureMipDataResult compressedTextureData = ElemCompressTextureMipData(ElemToolsGraphicsFormat_BC7, mipLevelData, NULL); + + DisplayOutputMessages("CompressTextureMipData", compressedTextureData.Messages); + + if (compressedTextureData.HasErrors) + { + return 1; + } + + uint32_t dataOffset = ftell(file); + fwrite(compressedTextureData.MipData.Data.Items, sizeof(uint8_t), compressedTextureData.MipData.Data.Length, file); + + mipDataOffsets[i] = (SampleTextureDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; } - printf("Encoded data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); + printf("Compressed data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); - printf("Writing texture data to: %s\n", outputPath); + fseek(file, sizeof(SampleTextureHeader), SEEK_SET); + fwrite(mipDataOffsets, sizeof(SampleTextureHeader), mipData.Length, file); - FILE* file = fopen(outputPath, "wb"); fclose(file); printf("Texture compiled in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); } diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index ecc9b358..b1124700 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -393,7 +393,8 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf typedef enum { - ElemToolsGraphicsFormat_R8G8B8A8_SRGB + ElemToolsGraphicsFormat_R8G8B8A8, + ElemToolsGraphicsFormat_BC7 } ElemToolsGraphicsFormat; typedef enum @@ -444,9 +445,22 @@ typedef struct bool HasErrors; } ElemGenerateTextureMipDataResult; -ElemToolsAPI ElemLoadTextureResult ElemLoadTexture(const char* path, const ElemLoadTextureOptions* options); +typedef struct +{ + uint32_t Reserved; +} ElemCompressTextureMipDataOptions; -ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, const ElemGenerateTextureMipDataOptions* options); +typedef struct +{ + ElemToolsGraphicsFormat Format; + ElemTextureMipData MipData; + ElemToolsMessageSpan Messages; + bool HasErrors; +} ElemCompressTextureMipDataResult; + +ElemToolsAPI ElemLoadTextureResult ElemLoadTexture(const char* path, const ElemLoadTextureOptions* options); +ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, const ElemTextureMipData* baseMip, const ElemGenerateTextureMipDataOptions* options); +ElemToolsAPI ElemCompressTextureMipDataResult ElemCompressTextureMipData(ElemToolsGraphicsFormat format, const ElemTextureMipData* mipData, const ElemCompressTextureMipDataOptions* options); #ifdef UseToolsLoader #ifndef ElementalToolsLoader diff --git a/src/ElementalTools/ElementalToolsLoader.c b/src/ElementalTools/ElementalToolsLoader.c index 30076ec3..6174f24d 100644 --- a/src/ElementalTools/ElementalToolsLoader.c +++ b/src/ElementalTools/ElementalToolsLoader.c @@ -29,7 +29,8 @@ typedef struct ElementalToolsFunctions ElemLoadSceneResult (*ElemLoadScene)(char const *, ElemLoadSceneOptions const *); ElemBuildMeshletResult (*ElemBuildMeshlets)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *); ElemLoadTextureResult (*ElemLoadTexture)(char const *, ElemLoadTextureOptions const *); - ElemGenerateTextureMipDataResult (*ElemGenerateTextureMipData)(ElemToolsGraphicsFormat, ElemTextureMipData, ElemGenerateTextureMipDataOptions const *); + ElemGenerateTextureMipDataResult (*ElemGenerateTextureMipData)(ElemToolsGraphicsFormat, ElemTextureMipData const *, ElemGenerateTextureMipDataOptions const *); + ElemCompressTextureMipDataResult (*ElemCompressTextureMipData)(ElemToolsGraphicsFormat, ElemTextureMipData const *, ElemCompressTextureMipDataOptions const *); } ElementalToolsFunctions; @@ -88,7 +89,8 @@ static bool LoadElementalToolsFunctionPointers(void) listElementalToolsFunctions.ElemLoadScene = (ElemLoadSceneResult (*)(char const *, ElemLoadSceneOptions const *))GetElementalToolsFunctionPointer("ElemLoadScene"); listElementalToolsFunctions.ElemBuildMeshlets = (ElemBuildMeshletResult (*)(ElemVertexBuffer, ElemUInt32Span, ElemBuildMeshletsOptions const *))GetElementalToolsFunctionPointer("ElemBuildMeshlets"); listElementalToolsFunctions.ElemLoadTexture = (ElemLoadTextureResult (*)(char const *, ElemLoadTextureOptions const *))GetElementalToolsFunctionPointer("ElemLoadTexture"); - listElementalToolsFunctions.ElemGenerateTextureMipData = (ElemGenerateTextureMipDataResult (*)(ElemToolsGraphicsFormat, ElemTextureMipData, ElemGenerateTextureMipDataOptions const *))GetElementalToolsFunctionPointer("ElemGenerateTextureMipData"); + listElementalToolsFunctions.ElemGenerateTextureMipData = (ElemGenerateTextureMipDataResult (*)(ElemToolsGraphicsFormat, ElemTextureMipData const *, ElemGenerateTextureMipDataOptions const *))GetElementalToolsFunctionPointer("ElemGenerateTextureMipData"); + listElementalToolsFunctions.ElemCompressTextureMipData = (ElemCompressTextureMipDataResult (*)(ElemToolsGraphicsFormat, ElemTextureMipData const *, ElemCompressTextureMipDataOptions const *))GetElementalToolsFunctionPointer("ElemCompressTextureMipData"); functionPointersLoadedElementalTools = 1; @@ -267,7 +269,7 @@ static inline ElemLoadTextureResult ElemLoadTexture(char const * path, ElemLoadT return listElementalToolsFunctions.ElemLoadTexture(path, options); } -static inline ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, ElemGenerateTextureMipDataOptions const * options) +static inline ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData const * baseMip, ElemGenerateTextureMipDataOptions const * options) { if (!LoadElementalToolsFunctionPointers()) { @@ -297,3 +299,34 @@ static inline ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemTo return listElementalToolsFunctions.ElemGenerateTextureMipData(format, baseMip, options); } + +static inline ElemCompressTextureMipDataResult ElemCompressTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData const * mipData, ElemCompressTextureMipDataOptions const * options) +{ + if (!LoadElementalToolsFunctionPointers()) + { + assert(libraryElementalTools); + + #ifdef __cplusplus + ElemCompressTextureMipDataResult result = {}; + #else + ElemCompressTextureMipDataResult result = (ElemCompressTextureMipDataResult){0}; + #endif + + return result; + } + + if (!listElementalToolsFunctions.ElemCompressTextureMipData) + { + assert(listElementalToolsFunctions.ElemCompressTextureMipData); + + #ifdef __cplusplus + ElemCompressTextureMipDataResult result = {}; + #else + ElemCompressTextureMipDataResult result = (ElemCompressTextureMipDataResult){0}; + #endif + + return result; + } + + return listElementalToolsFunctions.ElemCompressTextureMipData(format, mipData, options); +} diff --git a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h index 791271b8..6b7ba473 100644 --- a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h @@ -1,5 +1,6 @@ #pragma once #define _CRT_SECURE_NO_WARNINGS +#define NOMINMAX #include #include diff --git a/src/ElementalTools/Textures/TextureLoaderStb.cpp b/src/ElementalTools/Textures/TextureLoaderStb.cpp index 79d9b9ad..8ff639bb 100644 --- a/src/ElementalTools/Textures/TextureLoaderStb.cpp +++ b/src/ElementalTools/Textures/TextureLoaderStb.cpp @@ -42,7 +42,7 @@ ElemLoadTextureResult LoadStbTexture(const char* path, ElemLoadTextureFileFormat return { .FileFormat = fileFormat, - .Format = ElemToolsGraphicsFormat_R8G8B8A8_SRGB, + .Format = ElemToolsGraphicsFormat_R8G8B8A8, .Width = (uint32_t)width, .Height = (uint32_t)height, .MipData = { .Items = mipData, .Length = 1 }, diff --git a/src/ElementalTools/Textures/TextureProcessing.cpp b/src/ElementalTools/Textures/TextureProcessing.cpp index 06bbc53d..3304beab 100644 --- a/src/ElementalTools/Textures/TextureProcessing.cpp +++ b/src/ElementalTools/Textures/TextureProcessing.cpp @@ -1,11 +1,15 @@ #include "ElementalTools.h" +#include "ToolsUtils.h" #include "SystemMemory.h" #include "SystemFunctions.h" +#define TEXTURE_BC_BLOCK_SIZE_IN_BYTES 16 + // TODO: Do one for each thread static MemoryArena generateMipDataMemoryArena; +static MemoryArena compressMipDataMemoryArena; -void InitgenerateMipDataMemoryArena() +void InitGenerateMipDataMemoryArena() { if (generateMipDataMemoryArena.Storage == nullptr) { @@ -15,9 +19,19 @@ void InitgenerateMipDataMemoryArena() SystemClearMemoryArena(generateMipDataMemoryArena); } -ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, ElemTextureMipData baseMip, const ElemGenerateTextureMipDataOptions* options) +void InitCompressMipDataMemoryArena() { - InitgenerateMipDataMemoryArena(); + if (compressMipDataMemoryArena.Storage == nullptr) + { + compressMipDataMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + } + + SystemClearMemoryArena(compressMipDataMemoryArena); +} + +ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToolsGraphicsFormat format, const ElemTextureMipData* baseMip, const ElemGenerateTextureMipDataOptions* options) +{ + InitGenerateMipDataMemoryArena(); auto messages = SystemPushArray(generateMipDataMemoryArena, 1024); auto messageCount = 0u; @@ -31,16 +45,16 @@ ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToo } auto pixelSize = 4; // TODO: Check format first - auto maxDimension = SystemMax(baseMip.Width, baseMip.Height); + auto maxDimension = SystemMax(baseMip->Width, baseMip->Height); auto mipCount = (uint32_t)floorf(log2f((float)maxDimension)) + 1; auto mipData = SystemPushArray(generateMipDataMemoryArena, mipCount); - auto baseMipCopy = SystemDuplicateBuffer(generateMipDataMemoryArena, ReadOnlySpan(baseMip.Data.Items, baseMip.Data.Length)); + auto baseMipCopy = SystemDuplicateBuffer(generateMipDataMemoryArena, ReadOnlySpan(baseMip->Data.Items, baseMip->Data.Length)); mipData[0] = { - .Width = baseMip.Width, - .Height = baseMip.Height, + .Width = baseMip->Width, + .Height = baseMip->Height, .Data = { .Items = baseMipCopy.Pointer, .Length = (uint32_t)baseMipCopy.Length } }; @@ -48,12 +62,12 @@ ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToo { auto mipLevelData = &mipData[i]; - mipLevelData->Width = SystemMax(1u, baseMip.Width >> i); - mipLevelData->Height = SystemMax(1u, baseMip.Height >> i); + mipLevelData->Width = SystemMax(1u, baseMip->Width >> i); + mipLevelData->Height = SystemMax(1u, baseMip->Height >> i); auto mipLevelPixels = SystemPushArray(generateMipDataMemoryArena, mipLevelData->Width * mipLevelData->Height * pixelSize); - stbir_resize_uint8_srgb(baseMip.Data.Items, baseMip.Width, baseMip.Height, 0, + stbir_resize_uint8_srgb(baseMip->Data.Items, baseMip->Width, baseMip->Height, 0, mipLevelPixels.Pointer, mipLevelData->Width, mipLevelData->Height, 0, STBIR_RGBA); @@ -67,3 +81,81 @@ ElemToolsAPI ElemGenerateTextureMipDataResult ElemGenerateTextureMipData(ElemToo .HasErrors = hasErrors }; } + +ElemToolsAPI ElemCompressTextureMipDataResult ElemCompressTextureMipData(ElemToolsGraphicsFormat format, const ElemTextureMipData* mipData, const ElemCompressTextureMipDataOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + InitCompressMipDataMemoryArena(); + + auto messages = SystemPushArray(compressMipDataMemoryArena, 1024); + auto messageCount = 0u; + auto hasErrors = false; + + if (format != ElemToolsGraphicsFormat_BC7) + { + return { .Messages = ConstructErrorMessageSpan(compressMipDataMemoryArena, "The format of the mip data should be BC7 for now."), .HasErrors = true }; + } + + ElemCompressTextureMipDataOptions compressMipDataOptions = {}; + + if (options) + { + compressMipDataOptions = *options; + } + + // TODO: Fow now we don't use the GPU + bc7enc_compress_block_init(); + + bc7enc_compress_block_params bc7Params; + bc7enc_compress_block_params_init(&bc7Params); + bc7Params.m_uber_level = 4; // TODO: Do a global quality level option + //bc7Params.m_uber_level = 0; // TODO: Do a global quality level option + + auto blockWidth = (mipData->Width + 3) / 4; + auto blockHeight = (mipData->Height + 3) / 4; + auto blockCount = blockWidth * blockHeight; + + auto compressedData = SystemPushArray(compressMipDataMemoryArena, blockCount * TEXTURE_BC_BLOCK_SIZE_IN_BYTES); + auto sourceBlockData = SystemPushArray(stackMemoryArena, 4 * 4 * 4); // TODO: DEFINE + + for (uint32_t by = 0; by < blockHeight; by++) + { + for (uint32_t bx = 0; bx < blockWidth; bx++) + { + auto destinationPointer = &compressedData[(by * blockWidth + bx) * TEXTURE_BC_BLOCK_SIZE_IN_BYTES]; + + for (uint32_t row = 0; row < 4; row++) + { + auto sy = by * 4 + row; + sy = SystemMax(0u, SystemMin(sy, mipData->Height - 1)); + + for (uint32_t col = 0; col < 4; col++) + { + uint32_t sx = bx * 4 + col; + sx = SystemMax(0u, SystemMin(sx, mipData->Width - 1)); + + int srcIndex = (sy * mipData->Width + sx) * 4; + int dstIndex = (row * 4 + col) * 4; + sourceBlockData[dstIndex + 0] = mipData->Data.Items[srcIndex + 0]; + sourceBlockData[dstIndex + 1] = mipData->Data.Items[srcIndex + 1]; + sourceBlockData[dstIndex + 2] = mipData->Data.Items[srcIndex + 2]; + sourceBlockData[dstIndex + 3] = mipData->Data.Items[srcIndex + 3]; + } + } + + bc7enc_compress_block(destinationPointer, sourceBlockData.Pointer, &bc7Params); + } + } + + return + { + .MipData = + { + .Width = mipData->Width, + .Height = mipData->Height, + .Data = { .Items = compressedData.Pointer, .Length = (uint32_t)compressedData.Length } + }, + .Messages = { .Items = messages.Pointer, .Length = messageCount }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp index 513e5743..515f86d1 100644 --- a/src/ElementalTools/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -23,6 +23,8 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize2.h" +#include "bc7enc.cpp" + #include "Textures/TextureLoader.cpp" #include "Textures/TextureProcessing.cpp" From a1ec4b47203c863eee0561b8c446d643ed217322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 31 Dec 2024 14:58:06 +0100 Subject: [PATCH 49/93] Load blank textures from the scene in renderer. --- samples/Common/SampleGpuMemory.h | 34 +++++ samples/Common/SampleScene.h | 9 ++ samples/Common/SampleSceneLoader.h | 116 +++++++++++++++- samples/Common/SampleUtils.h | 124 ++++++------------ samples/Elemental/99-Renderer/main.c | 15 +++ .../ElementalTools/02-SceneCompiler/main.c | 29 +++- .../ElementalTools/03-TextureCompiler/main.c | 2 +- samples/XX-MinimalTriangle/main.c | 10 +- .../Apple/Graphics/MetalResource.cpp | 10 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 5 +- src/Elemental/Elemental.h | 15 +-- .../Microsoft/Graphics/DirectX12Resource.cpp | 10 +- src/ElementalTools/ElementalTools.h | 27 ++-- .../SceneLoading/SceneLoaderGltf.cpp | 1 + .../SceneLoading/SceneLoaderObj.cpp | 82 +++++++++--- .../Textures/TextureProcessing.cpp | 1 + tests/GraphicsTests/ResourceTests.cpp | 4 +- tests/ToolsTests/SceneLoaderTests.cpp | 2 + 18 files changed, 356 insertions(+), 140 deletions(-) diff --git a/samples/Common/SampleGpuMemory.h b/samples/Common/SampleGpuMemory.h index d821d4cc..b4da5e12 100644 --- a/samples/Common/SampleGpuMemory.h +++ b/samples/Common/SampleGpuMemory.h @@ -16,6 +16,12 @@ typedef struct ElemGraphicsResourceDescriptor ReadDescriptor; } SampleGpuBuffer; +typedef struct +{ + ElemGraphicsResource Texture; + ElemGraphicsResourceDescriptor ReadDescriptor; +} SampleGpuTexture; + SampleGpuMemory SampleCreateGpuMemory(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes) { // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues @@ -66,3 +72,31 @@ void SampleFreeGpuBuffer(SampleGpuBuffer* gpuBuffer) ElemFreeGraphicsResource(gpuBuffer->Buffer, NULL); gpuBuffer->Buffer = ELEM_HANDLE_NULL; } + +SampleGpuTexture SampleCreateGpuTexture(SampleGpuMemory* gpuMemory, uint32_t width, uint32_t height, uint32_t mipLevels, ElemGraphicsFormat format, const char* debugName) +{ + ElemGraphicsResourceInfo textureDescription = ElemCreateTexture2DResourceInfo(gpuMemory->GraphicsDevice, width, height, mipLevels, format, ElemGraphicsResourceUsage_Read, &(ElemGraphicsResourceInfoOptions) { .DebugName = debugName }); + + gpuMemory->CurrentHeapOffset = SampleAlignValue(gpuMemory->CurrentHeapOffset, textureDescription.Alignment); + ElemGraphicsResource texture = ElemCreateGraphicsResource(gpuMemory->GraphicsHeap, gpuMemory->CurrentHeapOffset, &textureDescription); + gpuMemory->CurrentHeapOffset += textureDescription.SizeInBytes; + + ElemGraphicsResourceDescriptor readDescriptor = ElemCreateGraphicsResourceDescriptor(texture, ElemGraphicsResourceDescriptorUsage_Read, NULL); + + return (SampleGpuTexture) + { + .Texture = texture, + .ReadDescriptor = readDescriptor + }; +} + +void SampleFreeGpuTexture(SampleGpuTexture* gpuTexture) +{ + assert(gpuTexture); + + ElemFreeGraphicsResourceDescriptor(gpuTexture->ReadDescriptor, NULL); + gpuTexture->ReadDescriptor = ELEM_HANDLE_NULL; + + ElemFreeGraphicsResource(gpuTexture->Texture, NULL); + gpuTexture->Texture = ELEM_HANDLE_NULL; +} diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h index b80c859a..b8ebc106 100644 --- a/samples/Common/SampleScene.h +++ b/samples/Common/SampleScene.h @@ -14,8 +14,17 @@ typedef struct char FileId[5]; uint32_t MeshCount; uint32_t NodeCount; + uint32_t MaterialCount; } SampleSceneHeader; +typedef struct +{ + char Name[50]; + char AlbedoTexturePath[255]; + char NormalTexturePath[255]; + ElemVector4 AlbedoFactor; +} SampleSceneMaterialHeader; + typedef struct { char Name[50]; diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 8761182f..795cb1b0 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -3,6 +3,7 @@ #include "Elemental.h" #include "SampleUtils.h" #include "SampleScene.h" +#include "SampleTexture.h" #include "SampleGpuMemory.h" // TODO: The struct here are temporary. The final data will be placed in buffer @@ -14,10 +15,28 @@ typedef struct SampleGpuBuffer MeshBuffer; } SampleMeshData; +typedef struct +{ + const char* Path; + SampleTextureHeader TextureHeader; + SampleTextureDataBlockEntry* MipDataEntries; + SampleGpuTexture GpuTexture; + bool IsLoaded; +} SampleTextureData; + +typedef struct +{ + SampleSceneMaterialHeader MaterialHeader; + SampleTextureData AlbedoTexture; + SampleTextureData NormalTexture; +} SampleMaterialData; + typedef struct { uint32_t MeshCount; SampleMeshData* Meshes; + uint32_t MaterialCount; + SampleMaterialData* Materials; uint32_t NodeCount; SampleSceneNodeHeader* Nodes; } SampleSceneData; @@ -37,7 +56,6 @@ void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) fread(&meshData->MeshHeader, sizeof(SampleMeshHeader), 1, file); - // TODO: Decode header // TODO: Can we get rid of the malloc? meshData->MeshPrimitives = (SampleMeshPrimitiveHeader*)malloc(sizeof(SampleMeshPrimitiveHeader) * meshData->MeshHeader.MeshPrimitiveCount); @@ -75,6 +93,80 @@ void SampleFreeMesh(SampleMeshData* meshData) // TODO: Free mallocs } +void SampleLoadTexture(const char* path, SampleTextureData* textureData) +{ + *textureData = (SampleTextureData){}; + + // TODO: Do better here + char* tmp = malloc(strlen(path)); + strcpy(tmp, path); + textureData->Path = tmp; + + FILE* file = SampleOpenFile(path, true); + assert(file); + + fread(&textureData->TextureHeader, sizeof(SampleTextureHeader), 1, file); + + textureData->MipDataEntries = (SampleTextureDataBlockEntry*)malloc(sizeof(SampleTextureDataBlockEntry) * textureData->TextureHeader.MipCount); + fread(textureData->MipDataEntries, sizeof(SampleTextureDataBlockEntry), textureData->TextureHeader.MipCount, file); + + fclose(file); +} + +// TODO: In the future we will need to load individual mips from disk +void SampleLoadTextureData(SampleTextureData* textureData, SampleGpuMemory* gpuMemory) +{ + assert(textureData->Path); + + // TODO: Get texture name without folder and extension + // TODO: Get the correct format + textureData->GpuTexture = SampleCreateGpuTexture(gpuMemory, textureData->TextureHeader.Width, textureData->TextureHeader.Height, textureData->TextureHeader.MipCount, ElemGraphicsFormat_BC7_SRGB, textureData->Path); + + FILE* file = SampleOpenFile(textureData->Path, true); + assert(file); + + //fseek(file, meshData->MeshHeader.MeshBufferOffset, SEEK_SET); + + fclose(file); + + textureData->IsLoaded = true; +} + +void SampleFreeTexture(SampleTextureData* textureData) +{ + if (textureData->GpuTexture.Texture != ELEM_HANDLE_NULL) + { + SampleFreeGpuTexture(&textureData->GpuTexture); + } + // TODO: Free mallocs +} + +void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleMaterialData* materialData, const char* directoryPath) +{ + *materialData = (SampleMaterialData){}; + materialData->MaterialHeader = *materialHeader; + + // TODO: Some materials can use the same texture so we need to load it only once + if (strlen(materialHeader->AlbedoTexturePath) > 0) + { + char fullTexturePath[MAX_PATH]; + strcpy(fullTexturePath, directoryPath); + strcat(fullTexturePath, materialHeader->AlbedoTexturePath); + + SampleLoadTexture(fullTexturePath, &materialData->AlbedoTexture); + } + + // TODO: NormalMap +} + +void SampleFreeMaterial(SampleMaterialData* materialData) +{ + if (materialData->AlbedoTexture.IsLoaded) + { + SampleFreeTexture(&materialData->AlbedoTexture); + } +} + void SampleLoadScene(const char* path, SampleSceneData* sceneData) { // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! @@ -90,9 +182,10 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) printf("ERROR: Wrong scene format\n"); } - printf("Scene Loaded: Meshes Count=%d, Nodes Count=%d\n", sceneHeader.MeshCount, sceneHeader.NodeCount); + printf("Scene Loaded: Meshes Count=%d, Material Count=%d, Nodes Count=%d\n", sceneHeader.MeshCount, sceneHeader.MaterialCount, sceneHeader.NodeCount); sceneData->MeshCount = sceneHeader.MeshCount; + sceneData->MaterialCount = sceneHeader.MaterialCount; sceneData->NodeCount = sceneHeader.NodeCount; // TODO: Replace malloc with an utility function that we will replace @@ -101,6 +194,10 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) SampleDataBlockEntry* meshDataBlocks = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * sceneHeader.MeshCount); fread(meshDataBlocks, sizeof(SampleDataBlockEntry), sceneHeader.MeshCount, file); + sceneData->Materials = (SampleMaterialData*)malloc(sizeof(SampleMaterialData) * sceneHeader.MaterialCount); + SampleSceneMaterialHeader* materialHeaders = (SampleSceneMaterialHeader*)malloc(sizeof(SampleSceneMaterialHeader) * sceneHeader.MaterialCount); + fread(materialHeaders, sizeof(SampleSceneMaterialHeader), sceneHeader.MaterialCount, file); + sceneData->Nodes = (SampleSceneNodeHeader*)malloc(sizeof(SampleSceneNodeHeader) * sceneHeader.NodeCount); fread(sceneData->Nodes, sizeof(SampleSceneNodeHeader), sceneHeader.NodeCount, file); @@ -109,9 +206,17 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) for (uint32_t i = 0; i < sceneData->MeshCount; i++) { SampleLoadMesh(path, meshDataBlocks[i].Offset, &sceneData->Meshes[i]); - //SampleLoadMeshData(file, gpuMemory, &sceneData->Meshes[i], "Mesh"); } + char directoryPath[MAX_PATH]; + GetFileDirectory(path, directoryPath, MAX_PATH); + + for (uint32_t i = 0; i < sceneData->MaterialCount; i++) + { + SampleLoadMaterial(&materialHeaders[i], &sceneData->Materials[i], directoryPath); + } + + free(materialHeaders); free(meshDataBlocks); } @@ -121,4 +226,9 @@ void SampleFreeScene(const SampleSceneData* sceneData) { SampleFreeMesh(&sceneData->Meshes[i]); } + + for (uint32_t i = 0; i < sceneData->MaterialCount; i++) + { + SampleFreeMaterial(&sceneData->Materials[i]); + } } diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index a4bd9ca2..cecbdb31 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -44,6 +44,7 @@ uint32_t SampleAlignValue(uint32_t value, uint32_t alignment) // ----------------------------------------------------------------------------- void SampleGetFullPath(char* destination, const char* path, bool prefixData) { + // TODO: Pass destination length memset(destination, 0, MAX_PATH); char* pointer = destination; @@ -167,29 +168,6 @@ int SampleWriteDataToFile(const char* filename, ElemDataSpan data, bool append) return 0; // Success } -uint32_t SampleGetFileSize(const char* filename) -{ - if (filename == NULL) - { - printf("ERROR 1\n"); - return -1; - } - - FILE* file = fopen(filename, "ab"); - - if (file == NULL) - { - printf("ERROR 2\n"); - return -1; - } - - fseek(file, 0, SEEK_END); - uint32_t fileSize = ftell(file); - fclose(file); - - return fileSize; -} - int SampleWriteDataToApplicationFile(const char* filename, ElemDataSpan data, bool append) { char absolutePath[MAX_PATH]; @@ -198,85 +176,65 @@ int SampleWriteDataToApplicationFile(const char* filename, ElemDataSpan data, bo return SampleWriteDataToFile(absolutePath, data, append); } -ElemDataSpan SampleReadLine(ElemDataSpan* data) +// TODO: Add Sample prefix +void ReplaceFileExtension(const char* path, const char* extension, char* destination, uint32_t destinationSize) { - for (uint32_t i = 0; i < data->Length; i++) - { - if (data->Items[i] == '\n') - { - ElemDataSpan result = - { - .Items = data->Items, - .Length = i - }; - - data->Items += i + 1; - data->Length -= i + 1; - return result; - } - } + assert(destinationSize >= strlen(path)); + memset(destination, 0, destinationSize); - return (ElemDataSpan) - { - .Items = NULL, - .Length = 0 - }; -} + const char* extensionSeparator = strrchr(path, '.'); + uint32_t prefixLength = extensionSeparator ? (extensionSeparator - path) : strlen(path); -void SamplePrintDataSpan(ElemDataSpan data) -{ - for (uint32_t i = 0; i < data.Length; i++) + for (uint32_t i = 0; i < prefixLength; i++) { - printf("%c", data.Items[i]); + destination[i] = path[i] == '\\' ? '/' : path[i]; } - - printf("\n"); + + strncat(destination, extension, prefixLength); } -void SampleSplitString(ElemDataSpan* destination, uint32_t* destinationCount, ElemDataSpan source, char separator) +void GetFileDirectory(const char* path, char* destination, uint32_t destinationSize) { - uint8_t* pointer = source.Items; - - for (uint32_t i = 0; i < source.Length; i++) + assert(destinationSize >= strlen(path)); + memset(destination, 0, destinationSize); + + for (uint32_t i = 0; i < strlen(path); i++) { - if (source.Items[i] == separator) - { - destination[(*destinationCount)++] = (ElemDataSpan) - { - .Items = pointer, - .Length = (source.Items + i) - pointer - }; - - if (i + 1 < source.Length) - { - pointer = source.Items + i + 1; - } - } + destination[i] = path[i] == '\\' ? '/' : path[i]; } - destination[(*destinationCount)++] = (ElemDataSpan) + char* lastSeparator = strrchr(destination, '/'); + + if (lastSeparator) { - .Items = pointer, - .Length = (source.Items + source.Length) - pointer - }; + *(lastSeparator + 1) = '\0'; + } + else + { + destination[0] = '\0'; + } } -bool SampleCompareString(ElemDataSpan data, const char* value) +void GetRelativeResourcePath(const char* mainPath, const char* path, const char* extension, char* destination, uint32_t destinationSize) { - if (strlen(value) != data.Length) - { - return false; - } + assert(destinationSize >= strlen(path)); + memset(destination, 0, destinationSize); - for (uint32_t i = 0; i < data.Length; i++) + char mainDirectory[destinationSize]; + GetFileDirectory(mainPath, mainDirectory, destinationSize); + + char resourcePath[MAX_PATH]; + ReplaceFileExtension(path, extension, resourcePath, sizeof(resourcePath)); + + size_t mainDirectoryLength = strlen(mainDirectory); + const char* startPath = resourcePath; + + if (strncmp(resourcePath, mainDirectory, mainDirectoryLength) == 0) { - if (data.Items[i] != value[i]) - { - return false; - } + startPath = resourcePath + mainDirectoryLength; } - return true; + strncpy(destination, startPath, startPath - resourcePath); } // ----------------------------------------------------------------------------- diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index d045659c..31504f4c 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -177,6 +177,8 @@ void FreeSample(void* payload) SavedState savedState = { .CameraState = applicationPayload->InputsCamera.State }; SampleWriteDataToApplicationFile("SavedState.bin", (ElemDataSpan) { .Items = (uint8_t*)&savedState, .Length = sizeof(SavedState) }, false); + + printf("Exit application...\n"); } void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) @@ -235,6 +237,19 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); + // TODO: Fow now, we load all the material textures in one shot + // We do it in the main loop now because later it will be a streaming system. + // Doing it in the main loop force us to decouple the loading + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) + { + SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; + + if (material->AlbedoTexture.Path && !material->AlbedoTexture.IsLoaded) + { + SampleLoadTextureData(&material->AlbedoTexture, &applicationPayload->GpuMemory); + } + } + // TODO: Construct a list of tasks on the cpu for now and do only one dispatch mesh with the total of tasks // Be carreful with the limit per dimension of 65000 for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 112e65a3..b6b0e633 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -67,7 +67,7 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) return true; } -bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) +bool WriteSceneData(FILE* file, ElemLoadSceneResult scene, const char* sceneInputPath) { assert(file); @@ -75,6 +75,7 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) { .FileId = { 'S', 'C', 'E', 'N', 'E' }, .MeshCount = scene.Meshes.Length, + .MaterialCount = scene.Materials.Length, .NodeCount = scene.Nodes.Length }; @@ -83,6 +84,30 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene) // TODO: Get rid of malloc? SampleDataBlockEntry* meshDataOffsets = (SampleDataBlockEntry*)malloc(sizeof(SampleDataBlockEntry) * scene.Meshes.Length); fwrite(meshDataOffsets, sizeof(SampleDataBlockEntry), scene.Meshes.Length, file); + + for (uint32_t i = 0; i < scene.Materials.Length; i++) + { + ElemSceneMaterial* material = &scene.Materials.Items[i]; + + SampleSceneMaterialHeader materialHeader = + { + .AlbedoFactor = material->AlbedoFactor + }; + + strncpy(materialHeader.Name, material->Name, 50); + + if (material->AlbedoTexturePath) + { + GetRelativeResourcePath(sceneInputPath, material->AlbedoTexturePath, ".texture", materialHeader.AlbedoTexturePath, 255); + } + + if (material->NormalTexturePath) + { + GetRelativeResourcePath(sceneInputPath, material->NormalTexturePath, ".texture", materialHeader.NormalTexturePath, 255); + } + + fwrite(&materialHeader, sizeof(SampleSceneMaterialHeader), 1, file); + } for (uint32_t i = 0; i < scene.Nodes.Length; i++) { @@ -205,7 +230,7 @@ int main(int argc, const char* argv[]) printf("Writing Scene data to: %s\n", outputPath); FILE* file = fopen(outputPath, "wb"); - WriteSceneData(file, scene); + WriteSceneData(file, scene, inputPath); fclose(file); printf("Scene compiled in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); } diff --git a/samples/ElementalTools/03-TextureCompiler/main.c b/samples/ElementalTools/03-TextureCompiler/main.c index a088b94f..04592dda 100644 --- a/samples/ElementalTools/03-TextureCompiler/main.c +++ b/samples/ElementalTools/03-TextureCompiler/main.c @@ -110,7 +110,7 @@ int main(int argc, const char* argv[]) printf("Compressed data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); fseek(file, sizeof(SampleTextureHeader), SEEK_SET); - fwrite(mipDataOffsets, sizeof(SampleTextureHeader), mipData.Length, file); + fwrite(mipDataOffsets, sizeof(SampleTextureDataBlockEntry), mipData.Length, file); fclose(file); printf("Texture compiled in %.2fs\n", (SampleGetTimerValueInMS() - initialTimer) / 1000.0); diff --git a/samples/XX-MinimalTriangle/main.c b/samples/XX-MinimalTriangle/main.c index 085976b7..3bb8ab62 100644 --- a/samples/XX-MinimalTriangle/main.c +++ b/samples/XX-MinimalTriangle/main.c @@ -49,8 +49,15 @@ typedef struct void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); +ElemToolsDataSpan LoadShaderDataHandler(const char* path) +{ + return (ElemToolsDataSpan) { .Items = (uint8_t*)shaderSource, .Length = strlen(shaderSource) }; +} + void InitSample(void* payload) { + ElemToolsConfigureFileIO(LoadShaderDataHandler); + ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; applicationPayload->Window = ElemCreateWindow(NULL); @@ -63,8 +70,7 @@ void InitSample(void* payload) ElemSystemInfo systemInfo = ElemGetSystemInfo(); ElemGraphicsDeviceInfo graphicsDeviceInfo = ElemGetGraphicsDeviceInfo(applicationPayload->GraphicsDevice); - ElemShaderSourceData shaderSourceData = { .ShaderLanguage = ElemShaderLanguage_Hlsl, .Data = { .Items = (uint8_t*)shaderSource, .Length = strlen(shaderSource) } }; - ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary((ElemToolsGraphicsApi)graphicsDeviceInfo.GraphicsApi, (ElemToolsPlatform)systemInfo.Platform, &shaderSourceData, NULL); + ElemShaderCompilationResult compilationResult = ElemCompileShaderLibrary((ElemToolsGraphicsApi)graphicsDeviceInfo.GraphicsApi, (ElemToolsPlatform)systemInfo.Platform, "Triangle.hlsl", NULL); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, (ElemDataSpan) { .Items = compilationResult.Data.Items, .Length = compilationResult.Data.Length }); diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index 6c042947..b191677f 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -52,7 +52,7 @@ MTL::PixelFormat ConvertToMetalResourceFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_B8G8R8A8_SRGB: return MTL::PixelFormatBGRA8Unorm_sRGB; - case ElemGraphicsFormat_B8G8R8A8_UNORM: + case ElemGraphicsFormat_B8G8R8A8: return MTL::PixelFormatBGRA8Unorm; case ElemGraphicsFormat_R16G16B16A16_FLOAT: @@ -64,6 +64,9 @@ MTL::PixelFormat ConvertToMetalResourceFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return MTL::PixelFormatDepth32Float; + case ElemGraphicsFormat_BC7_SRGB: + return MTL:PixelFormatBC7_RGBAUnorm_sRGB; + default: return MTL::PixelFormatR8Unorm; } @@ -77,7 +80,7 @@ ElemGraphicsFormat ConvertFromMetalResourceFormat(MTL::PixelFormat format) return ElemGraphicsFormat_B8G8R8A8_SRGB; case MTL::PixelFormatBGRA8Unorm: - return ElemGraphicsFormat_B8G8R8A8_UNORM; + return ElemGraphicsFormat_B8G8R8A8; case MTL::PixelFormatRGBA16Float: return ElemGraphicsFormat_R16G16B16A16_FLOAT; @@ -88,6 +91,9 @@ ElemGraphicsFormat ConvertFromMetalResourceFormat(MTL::PixelFormat format) case MTL::PixelFormatDepth32Float: return ElemGraphicsFormat_D32_FLOAT; + case MTL:PixelFormatBC7_RGBAUnorm_sRGB: + return ElemGraphicsFormat_BC7_SRGB; + default: return ElemGraphicsFormat_Raw; } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 22c19e01..7d533ad6 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -49,7 +49,7 @@ VkFormat ConvertToVulkanTextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_B8G8R8A8_SRGB: return VK_FORMAT_B8G8R8A8_SRGB; - case ElemGraphicsFormat_B8G8R8A8_UNORM: + case ElemGraphicsFormat_B8G8R8A8: return VK_FORMAT_B8G8R8A8_UNORM; case ElemGraphicsFormat_R16G16B16A16_FLOAT: @@ -61,6 +61,9 @@ VkFormat ConvertToVulkanTextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return VK_FORMAT_D32_SFLOAT; + case ElemGraphicsFormat_BC7_SRGB: + return VK_FORMAT_BC7_SRGB_BLOCK; + case ElemGraphicsFormat_Raw: return VK_FORMAT_UNDEFINED; } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 505125d3..ad311f9c 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -394,10 +394,11 @@ typedef enum { ElemGraphicsFormat_Raw, ElemGraphicsFormat_B8G8R8A8_SRGB, - ElemGraphicsFormat_B8G8R8A8_UNORM, + ElemGraphicsFormat_B8G8R8A8, ElemGraphicsFormat_R16G16B16A16_FLOAT, ElemGraphicsFormat_R32G32B32A32_FLOAT, - ElemGraphicsFormat_D32_FLOAT + ElemGraphicsFormat_D32_FLOAT, + ElemGraphicsFormat_BC7_SRGB } ElemGraphicsFormat; typedef enum @@ -1049,15 +1050,7 @@ ElemAPI ElemFence ElemExecuteCommandList(ElemCommandQueue commandQueue, ElemComm */ ElemAPI ElemFence ElemExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandListSpan commandLists, const ElemExecuteCommandListOptions* options); -// TODO: Add a create fence function for a given commanduque? - -// TODO: Still in draft! -ElemAPI ElemIOCommandQueue ElemCreateIOCommandQueue(const ElemIOCommandQueueOptions* options); -ElemAPI void ElemFreeIOCommandQueue(ElemIOCommandQueue ioQueue); -ElemAPI void ElemEnqueueIOCommand(ElemIOCommandQueue ioCommandQueue, const ElemIOCommandParameters* parameters); -ElemAPI ElemFence ElemSubmitIOCommandQueue(); - -ElemAPI ElemFence ElemExecuteIOCommands(); +// TODO: Add a create fence function for a given commandqueue? /** * Waits for a fence to reach its signaled state on the CPU, effectively synchronizing CPU and GPU operations. diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index b57c5279..af4c4f56 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -137,7 +137,7 @@ DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_B8G8R8A8_SRGB: return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; - case ElemGraphicsFormat_B8G8R8A8_UNORM: + case ElemGraphicsFormat_B8G8R8A8: return DXGI_FORMAT_B8G8R8A8_UNORM; case ElemGraphicsFormat_R16G16B16A16_FLOAT: @@ -149,6 +149,9 @@ DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return DXGI_FORMAT_D32_FLOAT; + case ElemGraphicsFormat_BC7_SRGB: + return DXGI_FORMAT_BC7_UNORM_SRGB; + case ElemGraphicsFormat_Raw: return DXGI_FORMAT_UNKNOWN; } @@ -162,7 +165,7 @@ ElemGraphicsFormat ConvertFromDirectX12TextureFormat(DXGI_FORMAT format) return ElemGraphicsFormat_B8G8R8A8_SRGB; case DXGI_FORMAT_B8G8R8A8_UNORM: - return ElemGraphicsFormat_B8G8R8A8_UNORM; + return ElemGraphicsFormat_B8G8R8A8; case DXGI_FORMAT_R16G16B16A16_FLOAT: return ElemGraphicsFormat_R16G16B16A16_FLOAT; @@ -173,6 +176,9 @@ ElemGraphicsFormat ConvertFromDirectX12TextureFormat(DXGI_FORMAT format) case DXGI_FORMAT_D32_FLOAT: return ElemGraphicsFormat_D32_FLOAT; + case DXGI_FORMAT_BC7_UNORM_SRGB: + return ElemGraphicsFormat_BC7_SRGB; + default: return ElemGraphicsFormat_Raw; } diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index b1124700..3a9a1206 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -189,17 +189,6 @@ typedef enum ElemShaderLanguage_MetalIR = 6 } ElemShaderLanguage; -/** - * Represents shader source data, including language and the actual code. - */ -typedef struct -{ - // Language of the shader source code. - ElemShaderLanguage ShaderLanguage; - // Raw shader source code data. - ElemToolsDataSpan Data; -} ElemShaderSourceData; - /** * Configuration options for compiling shaders. */ @@ -217,6 +206,7 @@ typedef struct */ typedef struct { + // TODO: Add source language // Compiled shader binary data. ElemToolsDataSpan Data; // Messages generated during the compilation. @@ -312,6 +302,20 @@ typedef struct uint32_t Length; } ElemSceneMeshSpan; +typedef struct +{ + const char* Name; + const char* AlbedoTexturePath; + const char* NormalTexturePath; + ElemToolsVector4 AlbedoFactor; +} ElemSceneMaterial; + +typedef struct +{ + ElemSceneMaterial* Items; + uint32_t Length; +} ElemSceneMaterialSpan; + typedef struct { const char* Name; @@ -335,6 +339,7 @@ typedef struct ElemSceneCoordinateSystem CoordinateSystem; ElemSceneMeshSpan Meshes; + ElemSceneMaterialSpan Materials; ElemSceneNodeSpan Nodes; ElemToolsMessageSpan Messages; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp index f1688457..5a22a49b 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp @@ -228,6 +228,7 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; } + // TODO: Children? for (uint32_t i = 0; i < data->nodes_count; i++) { auto sceneNode = &sceneNodes[i]; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index cb5911ea..2a040237 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -47,6 +47,7 @@ size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userDa auto readLength = SystemMin(sourceSpan.Length - objLoaderFileData->CurrentOffset, bytes); + // TODO: For the map_Dist that isn't parsed by the lib, maybe we can replace it with bump here? if (readLength > 0) { SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); @@ -179,33 +180,19 @@ void ApplyObjBoundingBoxInverseTranslation(ElemToolsVector3 translation, ElemToo boundingBox->MaxPoint = { boundingBox->MaxPoint.X - translation.X, boundingBox->MaxPoint.Y - translation.Y, boundingBox->MaxPoint.Z - translation.Y }; } -ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* options) +ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const ElemLoadSceneOptions* options) { - auto stackMemoryArena = SystemGetStackMemoryArena(); auto hasErrors = false; - auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); - auto callbacks = fastObjCallbacks - { - .file_open = FastObjFileOpen, - .file_close = FastObjFileClose, - .file_read = FastObjFileRead, - .file_size = FastObjFileSize - }; + auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); - auto objFileData = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); - auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); - auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); auto meshPrimitiveInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); - for (uint32_t i = 0; i < objFileData->material_count; i++) - { - printf("Material: %s\n", objFileData->materials[i].name); - } - for (uint32_t i = 0; i < objFileData->object_count; i++) { auto mesh = &meshes[i]; @@ -337,8 +324,6 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o DecomposeTransform(nodeTransform, &sceneNode->Scale, &sceneNode->Rotation, &sceneNode->Translation); } - ResetLoadFileDataMemory(); - return { .SceneFormat = ElemSceneFormat_Obj, @@ -348,3 +333,60 @@ ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* o .HasErrors = hasErrors }; } + +ElemSceneMaterialSpan LoadObjMaterials(const fastObjMesh* objFileData, const ElemLoadSceneOptions* options) +{ + auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); + auto materials = SystemPushArray(sceneLoaderMemoryArena, objFileData->material_count); + + for (uint32_t i = 0; i < objFileData->material_count; i++) + { + auto objMaterial = &objFileData->materials[i]; + auto material = &materials[i]; + + if (objMaterial->name) + { + material->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objMaterial->name)).Pointer; + } + else + { + material->Name = "Material"; + } + + if (objMaterial->map_Kd > 0) + { + material->AlbedoTexturePath = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objFileData->textures[objMaterial->map_Kd].path)).Pointer; + } + + if (objMaterial->map_bump > 0) + { + material->NormalTexturePath = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objFileData->textures[objMaterial->map_bump].path)).Pointer; + } + + material->AlbedoFactor = {{ 1.0f, 1.0f, 1.0f, 1.0f }}; + + printf("material: %s\n", objFileData->materials[i].name); + } + + return { .Items = materials.Pointer, .Length = (uint32_t)materials.Length }; +} + +ElemLoadSceneResult LoadObjScene(const char* path, const ElemLoadSceneOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto callbacks = fastObjCallbacks + { + .file_open = FastObjFileOpen, + .file_close = FastObjFileClose, + .file_read = FastObjFileRead, + .file_size = FastObjFileSize + }; + + auto objFileData = fast_obj_read_with_callbacks(path, &callbacks, &stackMemoryArena); + + auto result = LoadObjSceneAndNodes(objFileData, options); + result.Materials = LoadObjMaterials(objFileData, options); + + return result; +} diff --git a/src/ElementalTools/Textures/TextureProcessing.cpp b/src/ElementalTools/Textures/TextureProcessing.cpp index 3304beab..1cc660ee 100644 --- a/src/ElementalTools/Textures/TextureProcessing.cpp +++ b/src/ElementalTools/Textures/TextureProcessing.cpp @@ -5,6 +5,7 @@ #define TEXTURE_BC_BLOCK_SIZE_IN_BYTES 16 +// TODO: See example: https://github.com/Hork-Engine/Hork-Source/blob/97c3630480983b20bd054e06ca6c26ae2e87095a/Hork/Image/ImageEncoders.cpp // TODO: Do one for each thread static MemoryArena generateMipDataMemoryArena; static MemoryArena compressMipDataMemoryArena; diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index b7cdad67..d8dca8c0 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -457,7 +457,7 @@ UTEST(Resource, CreateTexture2DResourceInfo_RenderTargetWrite) auto width = 128u; auto height = 256u; auto mipLevels = 5u; - auto format = ElemGraphicsFormat_B8G8R8A8_UNORM; + auto format = ElemGraphicsFormat_B8G8R8A8; ElemGraphicsResourceInfoOptions options = { .DebugName = "TestTexture2D" }; // Act @@ -704,7 +704,7 @@ UTEST(Resource, CreateGraphicsResourceDescriptor_WriteWithTexture2DWrite) // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_UNORM, ElemGraphicsResourceUsage_Write, nullptr); + auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8, ElemGraphicsResourceUsage_Write, nullptr); auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); // Act diff --git a/tests/ToolsTests/SceneLoaderTests.cpp b/tests/ToolsTests/SceneLoaderTests.cpp index 406620c5..3f9d01a0 100644 --- a/tests/ToolsTests/SceneLoaderTests.cpp +++ b/tests/ToolsTests/SceneLoaderTests.cpp @@ -1,6 +1,8 @@ #include "ToolsTests.h" #include "utest.h" +// TODO: Add real test files on disk? + auto cubeObjSceneSource = R"(o Cube v 1.000000 -1.000000 -1.000000 v 1.000000 -1.000000 1.000000 From 795084df324af8947e5173ded18d358125b1d17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 31 Dec 2024 17:17:56 +0100 Subject: [PATCH 50/93] Refactor gpu buffer data upload/download --- samples/Common/SampleGpuMemory.h | 3 +- samples/Common/SampleMath.h | 21 +-- .../04-HelloCompute/Data/Fractal.hlsl | 2 +- samples/Elemental/05-HelloImGui/main.c | 15 +- samples/Elemental/06-HelloMesh/main.c | 3 +- samples/Elemental/99-Renderer/main.c | 5 +- .../Apple/Graphics/MetalResource.cpp | 29 +++- src/Elemental/Apple/Graphics/MetalResource.h | 4 +- src/Elemental/Common/Graphics/Resource.cpp | 9 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 58 +++++++- .../Common/Graphics/Vulkan/VulkanResource.h | 4 +- src/Elemental/Elemental.h | 18 ++- src/Elemental/ElementalLoader.c | 31 ++++- .../Microsoft/Graphics/DirectX12Resource.cpp | 50 ++++++- .../Microsoft/Graphics/DirectX12Resource.h | 4 +- tests/GraphicsTests/CMakeLists.txt | 2 +- tests/GraphicsTests/CommandListTests.cpp | 2 +- .../{Shaders => Data}/Assert.hlsl | 0 .../{Shaders => Data}/CommandListTests.hlsl | 0 .../{Shaders => Data}/RenderingTests.hlsl | 0 .../ResourceBarrierTests.hlsl | 0 .../{Shaders => Data}/ShaderTests.hlsl | 0 tests/GraphicsTests/RenderingTests.cpp | 14 +- tests/GraphicsTests/ResourceBarrierTests.cpp | 12 +- tests/GraphicsTests/ResourceTests.cpp | 130 +++++++++++++++++- tests/GraphicsTests/ShaderTests.cpp | 8 +- 26 files changed, 348 insertions(+), 76 deletions(-) rename tests/GraphicsTests/{Shaders => Data}/Assert.hlsl (100%) rename tests/GraphicsTests/{Shaders => Data}/CommandListTests.hlsl (100%) rename tests/GraphicsTests/{Shaders => Data}/RenderingTests.hlsl (100%) rename tests/GraphicsTests/{Shaders => Data}/ResourceBarrierTests.hlsl (100%) rename tests/GraphicsTests/{Shaders => Data}/ShaderTests.hlsl (100%) diff --git a/samples/Common/SampleGpuMemory.h b/samples/Common/SampleGpuMemory.h index b4da5e12..235df1ff 100644 --- a/samples/Common/SampleGpuMemory.h +++ b/samples/Common/SampleGpuMemory.h @@ -54,8 +54,7 @@ SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, c ElemGraphicsResourceDescriptor readDescriptor = ElemCreateGraphicsResourceDescriptor(buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(buffer); - memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); + ElemUploadGraphicsBufferData(buffer, 0, (ElemDataSpan) { .Items = (uint8_t*)dataPointer, .Length = sizeInBytes }); return (SampleGpuBuffer) { diff --git a/samples/Common/SampleMath.h b/samples/Common/SampleMath.h index 3abea847..7a0bf144 100644 --- a/samples/Common/SampleMath.h +++ b/samples/Common/SampleMath.h @@ -353,7 +353,7 @@ SampleMatrix4x4 SampleTransposeMatrix(SampleMatrix4x4 matrix) SampleMatrix4x4 SampleCreateRotationMatrix(float angle) { // BUG: Uninit values! - SampleMatrix4x4 result; + SampleMatrix4x4 result = {}; float c = cosf(angle); float s = sinf(angle); @@ -377,7 +377,7 @@ SampleMatrix4x4 SampleCreateRotationMatrix(float angle) SampleMatrix4x4 SampleCreateScaleMatrix(float scale) { // BUG: Uninit values! - SampleMatrix4x4 result; + SampleMatrix4x4 result = {}; result.m[0][0] = scale; result.m[0][1] = 0.0f; @@ -399,21 +399,8 @@ SampleMatrix4x4 SampleCreateScaleMatrix(float scale) SampleMatrix4x4 SampleCreateTranslationMatrix(float tx, float ty) { // BUG: Uninit values! - SampleMatrix4x4 result; - - result.m[0][0] = 1.0f; - result.m[0][1] = 0.0f; - result.m[0][2] = tx; - - result.m[1][0] = 0.0f; - result.m[1][1] = 1.0f; - result.m[1][2] = ty; - - result.m[2][0] = 0.0f; - result.m[2][1] = 0.0f; - result.m[2][2] = 1.0f; - - result.m[3][3] = 1.0f; + SampleMatrix4x4 result = SampleCreateIdentityMatrix(); + result.Rows[3].XY = (SampleVector2) { tx, ty }; return result; } diff --git a/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl b/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl index 32fdd6e8..817782ed 100644 --- a/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl +++ b/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl @@ -93,7 +93,7 @@ void Fractal(uint3 threadId: SV_DispatchThreadID) uv.x *= aspectRatio; uv *= parameters.Zoom; - uv = mul(float3(uv, 1.0), parameters.Transform); + uv = mul(float4(uv, 0.0, 1.0), parameters.Transform); float gradient = ComputeJuliaSet(uv); //float gradient = ComputeMandelbrotSet(uv); diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c index 3a808ebb..023d889b 100644 --- a/samples/Elemental/05-HelloImGui/main.c +++ b/samples/Elemental/05-HelloImGui/main.c @@ -155,10 +155,7 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; uint32_t currentVertexOffset = 0; - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(imGuiData->VertexBuffer); - uint32_t currentIndexOffset = 0; - ElemDataSpan indexBufferPointer = ElemGetGraphicsResourceDataSpan(imGuiData->IndexBuffer); for (int32_t i = 0; i < drawData->CmdListsCount; i++) { @@ -193,10 +190,18 @@ void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, Appl ElemDispatchMesh(commandList, dispatchCount, 1, 1); } - memcpy(vertexBufferPointer.Items + currentVertexOffset * sizeof(ImDrawVert), imCommandList->VtxBuffer.Data, imCommandList->VtxBuffer.Size * sizeof(ImDrawVert)); + ElemUploadGraphicsBufferData(imGuiData->VertexBuffer, currentVertexOffset * sizeof(ImDrawVert), (ElemDataSpan) { + .Items = (uint8_t*)imCommandList->VtxBuffer.Data, + .Length = imCommandList->VtxBuffer.Size * sizeof(ImDrawVert) + }); + currentVertexOffset += imCommandList->VtxBuffer.Size; - memcpy(indexBufferPointer.Items + currentIndexOffset * sizeof(ImDrawIdx), imCommandList->IdxBuffer.Data, imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx)); + ElemUploadGraphicsBufferData(imGuiData->IndexBuffer, currentIndexOffset * sizeof(ImDrawIdx), (ElemDataSpan) { + .Items = (uint8_t*)imCommandList->IdxBuffer.Data, + .Length = imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx) + }); + currentIndexOffset += imCommandList->IdxBuffer.Size; } } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index 7a4f5042..f517b711 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -79,8 +79,7 @@ void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceD *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(*buffer); - memcpy(vertexBufferPointer.Items, dataPointer, sizeInBytes); + ElemUploadGraphicsBufferData(*buffer, 0, (ElemDataSpan) { .Items = dataPointer, .Length = sizeInBytes }); } void InitSample(void* payload) diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 31504f4c..cfae5285 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -86,9 +86,8 @@ void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 vie { applicationPayload->FrameData.ViewProjMatrix = viewProjMatrix; applicationPayload->FrameData.ShowMeshlets = showMeshlets; - - ElemDataSpan vertexBufferPointer = ElemGetGraphicsResourceDataSpan(applicationPayload->FrameDataBuffer.Buffer); - memcpy(vertexBufferPointer.Items, &applicationPayload->FrameData, sizeof(ShaderFrameData)); + + ElemUploadGraphicsBufferData(applicationPayload->FrameDataBuffer.Buffer, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->FrameData, .Length = sizeof(ShaderFrameData) }); } void InitSample(void* payload) diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index b191677f..03d856a2 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -420,7 +420,7 @@ ElemGraphicsResourceInfo MetalGetGraphicsResourceInfo(ElemGraphicsResource resou .Usage = resourceData->Usage }; } - +/* ElemDataSpan MetalGetGraphicsResourceDataSpan(ElemGraphicsResource resource) { SystemAssert(resource != ELEM_HANDLE_NULL); @@ -441,6 +441,33 @@ ElemDataSpan MetalGetGraphicsResourceDataSpan(ElemGraphicsResource resource) .Items = (uint8_t*)dataPointer, .Length = resourceData->Width }; +}*/ + +void MetalUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) +{ +} + +ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) +{ + /*auto offset = 0u; + auto sizeInBytes = resourceData->Width; + + if (options) + { + offset = options->Offset; + + if (options->SizeInBytes != 0) + { + sizeInBytes = options->SizeInBytes; + } + } + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto downloadedData = SystemPushArray(directX12ReadBackMemoryArena, sizeInBytes); + memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); + + return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length };*/ + return {}; } ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index 6bb0b45a..7ef36b16 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -48,7 +48,9 @@ ElemGraphicsResourceInfo MetalCreateTexture2DResourceInfo(ElemGraphicsDevice gra ElemGraphicsResource MetalCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, uint64_t graphicsHeapOffset, const ElemGraphicsResourceInfo* resourceInfo); void MetalFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options); ElemGraphicsResourceInfo MetalGetGraphicsResourceInfo(ElemGraphicsResource resource); -ElemDataSpan MetalGetGraphicsResourceDataSpan(ElemGraphicsResource resource); + +void MetalUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); +ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo MetalGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/Common/Graphics/Resource.cpp b/src/Elemental/Common/Graphics/Resource.cpp index 3241f5ad..ebf655e5 100644 --- a/src/Elemental/Common/Graphics/Resource.cpp +++ b/src/Elemental/Common/Graphics/Resource.cpp @@ -46,9 +46,14 @@ ElemAPI ElemGraphicsResourceInfo ElemGetGraphicsResourceInfo(ElemGraphicsResourc DispatchReturnGraphicsFunction(GetGraphicsResourceInfo, resource); } -ElemAPI ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource resource) +ElemAPI void ElemUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) { - DispatchReturnGraphicsFunction(GetGraphicsResourceDataSpan, resource); + DispatchGraphicsFunction(UploadGraphicsBufferData, resource, offset, data); +} + +ElemAPI ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) +{ + DispatchReturnGraphicsFunction(DownloadGraphicsBufferData, resource, options); } ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 7d533ad6..f9a51621 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -15,6 +15,7 @@ SystemDataPool vulka Span vulkanResourceDescriptorInfos; // TODO: To refactor Span vulkanResourceDescriptorImageViews; +MemoryArena vulkanReadBackMemoryArena; void InitVulkanResourceMemory() { @@ -24,6 +25,7 @@ void InitVulkanResourceMemory() vulkanGraphicsResourcePool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); vulkanResourceDescriptorInfos = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); vulkanResourceDescriptorImageViews = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); + vulkanReadBackMemoryArena = SystemAllocateMemoryArena(); } } @@ -510,7 +512,7 @@ ElemGraphicsResourceInfo VulkanGetGraphicsResourceInfo(ElemGraphicsResource reso }; } -ElemDataSpan VulkanGetGraphicsResourceDataSpan(ElemGraphicsResource resource) +void VulkanUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) { SystemAssert(resource != ELEM_HANDLE_NULL); @@ -519,7 +521,39 @@ ElemDataSpan VulkanGetGraphicsResourceDataSpan(ElemGraphicsResource resource) if (resourceData->Type != ElemGraphicsResourceType_Buffer) { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GetGraphicsResourceDataSpan only works with graphics buffers."); + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers."); + return; + } + + if (resourceData->CpuDataPointer == nullptr) + { + auto resourceDataFull = GetVulkanGraphicsResourceDataFull(resource); + SystemAssert(resourceDataFull); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(resourceDataFull->GraphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); + SystemAssert(graphicsHeapData); + + // TODO: use vkMapMemory2 just for consistency + AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset, resourceData->Width, 0, &resourceData->CpuDataPointer)); + } + + auto destinationPointer = (uint8_t*)resourceData->CpuDataPointer + offset; + memcpy(destinationPointer, data.Items, data.Length); +} + +ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) +{ + SystemAssert(resource != ELEM_HANDLE_NULL); + + auto resourceData = GetVulkanGraphicsResourceData(resource); + SystemAssert(resourceData); + + if (resourceData->Type != ElemGraphicsResourceType_Buffer) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "DownloadGraphicsBufferData only works with graphics buffers."); return {}; } @@ -538,7 +572,24 @@ ElemDataSpan VulkanGetGraphicsResourceDataSpan(ElemGraphicsResource resource) AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset, resourceData->Width, 0, &resourceData->CpuDataPointer)); } - return { .Items = (uint8_t*)resourceData->CpuDataPointer, .Length = resourceData->Width }; + auto offset = 0u; + auto sizeInBytes = resourceData->Width; + + if (options) + { + offset = options->Offset; + + if (options->SizeInBytes != 0) + { + sizeInBytes = options->SizeInBytes; + } + } + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto downloadedData = SystemPushArray(vulkanReadBackMemoryArena, sizeInBytes); + memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); + + return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) @@ -679,4 +730,5 @@ void VulkanFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descrip void VulkanProcessGraphicsResourceDeleteQueue() { ProcessResourceDeleteQueue(); + SystemClearMemoryArena(vulkanReadBackMemoryArena); } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index 2770199a..05424bba 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -52,7 +52,9 @@ ElemGraphicsResourceInfo VulkanCreateTexture2DResourceInfo(ElemGraphicsDevice gr ElemGraphicsResource VulkanCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, uint64_t graphicsHeapOffset, const ElemGraphicsResourceInfo* resourceInfo); void VulkanFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options); ElemGraphicsResourceInfo VulkanGetGraphicsResourceInfo(ElemGraphicsResource resource); -ElemDataSpan VulkanGetGraphicsResourceDataSpan(ElemGraphicsResource resource); + +void VulkanUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); +ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo VulkanGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index ad311f9c..0c79140a 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -358,7 +358,9 @@ typedef enum // Command queue for graphics operations. ElemCommandQueueType_Graphics = 0, // Command queue for compute operations. - ElemCommandQueueType_Compute = 1 + ElemCommandQueueType_Compute = 1, + // TODO: Create a dedicated IO queue + //ElemCommandQueueType_IO = 2 } ElemCommandQueueType; // TODO: Not needed? @@ -749,6 +751,12 @@ typedef struct ElemFenceSpan FencesToWait; } ElemFreeGraphicsResourceDescriptorOptions; +typedef struct +{ + uint32_t Offset; + uint32_t SizeInBytes; +} ElemDownloadGraphicsBufferDataOptions; + typedef struct { ElemGraphicsFormat Format; @@ -1099,6 +1107,7 @@ ElemAPI void ElemPresentSwapChain(ElemSwapChain swapChain); ElemAPI ElemGraphicsHeap ElemCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes, const ElemGraphicsHeapOptions* options); ElemAPI void ElemFreeGraphicsHeap(ElemGraphicsHeap graphicsHeap); +// TODO: uint64_t for sizeInBytes? ElemAPI ElemGraphicsResourceInfo ElemCreateGraphicsBufferResourceInfo(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsResourceUsage usage, const ElemGraphicsResourceInfoOptions* options); ElemAPI ElemGraphicsResourceInfo ElemCreateTexture2DResourceInfo(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, uint32_t mipLevels, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage, const ElemGraphicsResourceInfoOptions* options); @@ -1106,9 +1115,10 @@ ElemAPI ElemGraphicsResource ElemCreateGraphicsResource(ElemGraphicsHeap graphic ElemAPI void ElemFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options); ElemAPI ElemGraphicsResourceInfo ElemGetGraphicsResourceInfo(ElemGraphicsResource resource); -// TODO: Rename taht UploadDataToGraphicsBuffer and pass as a parameter the span in input. We will issue the memcpy, it is too dangerous to manipulate the span -// TODO: We need to make sure then name explain that it is a direct upload without a command list and queue! -ElemAPI ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource resource); +// TODO: uint64_t for offset? +ElemAPI void ElemUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); +ElemAPI ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); +//ElemAPI ElemFence ElemCopyDataToGraphicsResource(ElemCommandQueue commandQueue); ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemAPI ElemGraphicsResourceDescriptorInfo ElemGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index 395e75e6..57428eb0 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -61,7 +61,8 @@ typedef struct ElementalFunctions ElemGraphicsResource (*ElemCreateGraphicsResource)(ElemGraphicsHeap, unsigned long long, ElemGraphicsResourceInfo const *); void (*ElemFreeGraphicsResource)(ElemGraphicsResource, ElemFreeGraphicsResourceOptions const *); ElemGraphicsResourceInfo (*ElemGetGraphicsResourceInfo)(ElemGraphicsResource); - ElemDataSpan (*ElemGetGraphicsResourceDataSpan)(ElemGraphicsResource); + void (*ElemUploadGraphicsBufferData)(ElemGraphicsResource, unsigned int, ElemDataSpan); + ElemDataSpan (*ElemDownloadGraphicsBufferData)(ElemGraphicsResource, ElemDownloadGraphicsBufferDataOptions const *); ElemGraphicsResourceDescriptor (*ElemCreateGraphicsResourceDescriptor)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *); ElemGraphicsResourceDescriptorInfo (*ElemGetGraphicsResourceDescriptorInfo)(ElemGraphicsResourceDescriptor); void (*ElemFreeGraphicsResourceDescriptor)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *); @@ -174,7 +175,8 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemCreateGraphicsResource = (ElemGraphicsResource (*)(ElemGraphicsHeap, unsigned long long, ElemGraphicsResourceInfo const *))GetElementalFunctionPointer("ElemCreateGraphicsResource"); listElementalFunctions.ElemFreeGraphicsResource = (void (*)(ElemGraphicsResource, ElemFreeGraphicsResourceOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResource"); listElementalFunctions.ElemGetGraphicsResourceInfo = (ElemGraphicsResourceInfo (*)(ElemGraphicsResource))GetElementalFunctionPointer("ElemGetGraphicsResourceInfo"); - listElementalFunctions.ElemGetGraphicsResourceDataSpan = (ElemDataSpan (*)(ElemGraphicsResource))GetElementalFunctionPointer("ElemGetGraphicsResourceDataSpan"); + listElementalFunctions.ElemUploadGraphicsBufferData = (void (*)(ElemGraphicsResource, unsigned int, ElemDataSpan))GetElementalFunctionPointer("ElemUploadGraphicsBufferData"); + listElementalFunctions.ElemDownloadGraphicsBufferData = (ElemDataSpan (*)(ElemGraphicsResource, ElemDownloadGraphicsBufferDataOptions const *))GetElementalFunctionPointer("ElemDownloadGraphicsBufferData"); listElementalFunctions.ElemCreateGraphicsResourceDescriptor = (ElemGraphicsResourceDescriptor (*)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsResourceDescriptor"); listElementalFunctions.ElemGetGraphicsResourceDescriptorInfo = (ElemGraphicsResourceDescriptorInfo (*)(ElemGraphicsResourceDescriptor))GetElementalFunctionPointer("ElemGetGraphicsResourceDescriptorInfo"); listElementalFunctions.ElemFreeGraphicsResourceDescriptor = (void (*)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResourceDescriptor"); @@ -1216,7 +1218,24 @@ static inline ElemGraphicsResourceInfo ElemGetGraphicsResourceInfo(ElemGraphicsR return listElementalFunctions.ElemGetGraphicsResourceInfo(resource); } -static inline ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource resource) +static inline void ElemUploadGraphicsBufferData(ElemGraphicsResource resource, unsigned int offset, ElemDataSpan data) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + return; + } + + if (!listElementalFunctions.ElemUploadGraphicsBufferData) + { + assert(listElementalFunctions.ElemUploadGraphicsBufferData); + return; + } + + listElementalFunctions.ElemUploadGraphicsBufferData(resource, offset, data); +} + +static inline ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource resource, ElemDownloadGraphicsBufferDataOptions const * options) { if (!LoadElementalFunctionPointers()) { @@ -1231,9 +1250,9 @@ static inline ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource return result; } - if (!listElementalFunctions.ElemGetGraphicsResourceDataSpan) + if (!listElementalFunctions.ElemDownloadGraphicsBufferData) { - assert(listElementalFunctions.ElemGetGraphicsResourceDataSpan); + assert(listElementalFunctions.ElemDownloadGraphicsBufferData); #ifdef __cplusplus ElemDataSpan result = {}; @@ -1244,7 +1263,7 @@ static inline ElemDataSpan ElemGetGraphicsResourceDataSpan(ElemGraphicsResource return result; } - return listElementalFunctions.ElemGetGraphicsResourceDataSpan(resource); + return listElementalFunctions.ElemDownloadGraphicsBufferData(resource, options); } static inline ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, ElemGraphicsResourceDescriptorOptions const * options) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index af4c4f56..867e8ea7 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -13,6 +13,7 @@ SystemDataPool // TODO: This descriptor infos should be linked to the graphics device like the resource desc heaps Span directX12ResourceDescriptorInfos; +MemoryArena directX12ReadBackMemoryArena; void InitDirectX12ResourceMemory() { @@ -21,6 +22,8 @@ void InitDirectX12ResourceMemory() directX12GraphicsHeapPool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_GRAPHICSHEAP); directX12GraphicsResourcePool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES); directX12ResourceDescriptorInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES); + + directX12ReadBackMemoryArena = SystemAllocateMemoryArena(); } } @@ -548,7 +551,30 @@ ElemGraphicsResourceInfo DirectX12GetGraphicsResourceInfo(ElemGraphicsResource r }; } -ElemDataSpan DirectX12GetGraphicsResourceDataSpan(ElemGraphicsResource resource) +void DirectX12UploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) +{ + SystemAssert(resource != ELEM_HANDLE_NULL); + + auto resourceData = GetDirectX12GraphicsResourceData(resource); + SystemAssert(resourceData); + + if (resourceData->Type != ElemGraphicsResourceType_Buffer) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers."); + return; + } + + if (resourceData->CpuDataPointer == nullptr) + { + D3D12_RANGE readRange = { 0, 0 }; + resourceData->DeviceObject->Map(0, &readRange, &resourceData->CpuDataPointer); + } + + auto destinationPointer = (uint8_t*)resourceData->CpuDataPointer + offset; + memcpy(destinationPointer, data.Items, data.Length); +} + +ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) { SystemAssert(resource != ELEM_HANDLE_NULL); @@ -557,7 +583,7 @@ ElemDataSpan DirectX12GetGraphicsResourceDataSpan(ElemGraphicsResource resource) if (resourceData->Type != ElemGraphicsResourceType_Buffer) { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GetGraphicsResourceDataSpan only works with graphics buffers."); + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers."); return {}; } @@ -567,7 +593,24 @@ ElemDataSpan DirectX12GetGraphicsResourceDataSpan(ElemGraphicsResource resource) resourceData->DeviceObject->Map(0, &readRange, &resourceData->CpuDataPointer); } - return { .Items = (uint8_t*)resourceData->CpuDataPointer, .Length = resourceData->Width }; + auto offset = 0u; + auto sizeInBytes = resourceData->Width; + + if (options) + { + offset = options->Offset; + + if (options->SizeInBytes != 0) + { + sizeInBytes = options->SizeInBytes; + } + } + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto downloadedData = SystemPushArray(directX12ReadBackMemoryArena, sizeInBytes); + memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); + + return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) @@ -711,4 +754,5 @@ void DirectX12FreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor desc void DirectX12ProcessGraphicsResourceDeleteQueue() { ProcessResourceDeleteQueue(); + SystemClearMemoryArena(directX12ReadBackMemoryArena); } diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index a298d62e..536e8f5e 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -54,7 +54,9 @@ ElemGraphicsResourceInfo DirectX12CreateTexture2DResourceInfo(ElemGraphicsDevice ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHeap, uint64_t graphicsHeapOffset, const ElemGraphicsResourceInfo* resourceInfo); void DirectX12FreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options); ElemGraphicsResourceInfo DirectX12GetGraphicsResourceInfo(ElemGraphicsResource resource); -ElemDataSpan DirectX12GetGraphicsResourceDataSpan(ElemGraphicsResource resource); + +void DirectX12UploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); +ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo DirectX12GetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/tests/GraphicsTests/CMakeLists.txt b/tests/GraphicsTests/CMakeLists.txt index 6133a8f2..a02bdb6d 100644 --- a/tests/GraphicsTests/CMakeLists.txt +++ b/tests/GraphicsTests/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(GraphicsTests UnityBuild.cpp Shaders) +add_executable(GraphicsTests UnityBuild.cpp Data) target_link_libraries(GraphicsTests PRIVATE utest) target_link_libraries(GraphicsTests PRIVATE ElementalInterface) diff --git a/tests/GraphicsTests/CommandListTests.cpp b/tests/GraphicsTests/CommandListTests.cpp index 52e719f4..c8342e90 100644 --- a/tests/GraphicsTests/CommandListTests.cpp +++ b/tests/GraphicsTests/CommandListTests.cpp @@ -122,7 +122,7 @@ UTEST(CommandList, ExecuteCommandListWaitForFence) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readBufferDataPipelineState); ElemFreePipelineState(writeBufferDataPipelineState); diff --git a/tests/GraphicsTests/Shaders/Assert.hlsl b/tests/GraphicsTests/Data/Assert.hlsl similarity index 100% rename from tests/GraphicsTests/Shaders/Assert.hlsl rename to tests/GraphicsTests/Data/Assert.hlsl diff --git a/tests/GraphicsTests/Shaders/CommandListTests.hlsl b/tests/GraphicsTests/Data/CommandListTests.hlsl similarity index 100% rename from tests/GraphicsTests/Shaders/CommandListTests.hlsl rename to tests/GraphicsTests/Data/CommandListTests.hlsl diff --git a/tests/GraphicsTests/Shaders/RenderingTests.hlsl b/tests/GraphicsTests/Data/RenderingTests.hlsl similarity index 100% rename from tests/GraphicsTests/Shaders/RenderingTests.hlsl rename to tests/GraphicsTests/Data/RenderingTests.hlsl diff --git a/tests/GraphicsTests/Shaders/ResourceBarrierTests.hlsl b/tests/GraphicsTests/Data/ResourceBarrierTests.hlsl similarity index 100% rename from tests/GraphicsTests/Shaders/ResourceBarrierTests.hlsl rename to tests/GraphicsTests/Data/ResourceBarrierTests.hlsl diff --git a/tests/GraphicsTests/Shaders/ShaderTests.hlsl b/tests/GraphicsTests/Data/ShaderTests.hlsl similarity index 100% rename from tests/GraphicsTests/Shaders/ShaderTests.hlsl rename to tests/GraphicsTests/Data/ShaderTests.hlsl diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index d3c19ea4..53ab534e 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -45,7 +45,7 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -87,7 +87,7 @@ UTEST(Rendering, RenderPassClearDepthBuffer) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)depthBuffer.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTextureFloat", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(depthBuffer); @@ -151,7 +151,7 @@ UTEST(Rendering, DispatchMesh) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -223,7 +223,7 @@ UTEST(Rendering, BeginRenderPass_SetViewport) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -298,7 +298,7 @@ UTEST(Rendering, BeginRenderPass_SetScissorRectangle) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -369,7 +369,7 @@ UTEST(Rendering, SetViewport) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -440,7 +440,7 @@ UTEST(Rendering, SetScissorRectangle) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); diff --git a/tests/GraphicsTests/ResourceBarrierTests.cpp b/tests/GraphicsTests/ResourceBarrierTests.cpp index e08ef295..1b6029bf 100644 --- a/tests/GraphicsTests/ResourceBarrierTests.cpp +++ b/tests/GraphicsTests/ResourceBarrierTests.cpp @@ -48,7 +48,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_BufferReadAfterWrite) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readBufferDataPipelineState); ElemFreePipelineState(writeBufferDataPipelineState); @@ -122,7 +122,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_BufferWriteAfterWrite) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readBufferDataPipelineState); ElemFreePipelineState(writeAddBufferDataPipelineState); @@ -201,7 +201,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_DifferentBuffers) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readBufferDataPipelineState); ElemFreePipelineState(writeBufferDataPipelineState); @@ -269,7 +269,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureReadAfterWrite) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readTextureDataPipelineState); ElemFreePipelineState(writeTextureDataPipelineState); @@ -379,7 +379,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_TextureRenderTargetAfterWrite) // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readTextureDataPipelineState); ElemFreePipelineState(writeTextureDataPipelineState); @@ -721,7 +721,7 @@ UTEST(ResourceBarrier, GraphicsResourceBarrier_BufferReadAfterWriteMultiCommandL // Assert ElemWaitForFenceOnCpu(fence); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); ElemFreePipelineState(readBufferDataPipelineState); ElemFreePipelineState(writeBufferDataPipelineState); diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index d8dca8c0..a45965fd 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -479,7 +479,7 @@ UTEST(Resource, CreateTexture2DResourceInfo_RenderTargetWrite) ASSERT_STREQ_MSG(resourceInfo.DebugName, "TestTexture2D", "Debug name should match the creation usage."); } -UTEST(Resource, GetGraphicsResourceDataSpan_WithBuffer) +UTEST(Resource, ElemUploadGraphicsBufferData_WithBuffer) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -492,9 +492,91 @@ UTEST(Resource, GetGraphicsResourceDataSpan_WithBuffer) auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); +} + +UTEST(Resource, ElemUploadGraphicsBufferData_WithBufferOffsetSize) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t data[] = { 1, 2, 3, 4 }; + ElemUploadGraphicsBufferData(resource, 2, { .Items = data, .Length = 2 }); // Act - auto resourceDataSpan = ElemGetGraphicsResourceDataSpan(resource); + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); + ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); +} + +UTEST(Resource, ElemUploadGraphicsBufferData_WithTexture2D) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_SRGB, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers."); +} + +UTEST(Resource, ElemDownloadGraphicsBufferData_WithBuffer) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); // Assert ElemFreeGraphicsResource(resource, nullptr); @@ -507,7 +589,45 @@ UTEST(Resource, GetGraphicsResourceDataSpan_WithBuffer) ASSERT_EQ_MSG(resourceDataSpan.Length, 1024u, "Resource dataspan length should be equals to buffer size."); } -UTEST(Resource, GetGraphicsResourceDataSpan_WithTexture2D) +UTEST(Resource, ElemDownloadGraphicsBufferData_WithBufferOffsetAndSize) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t data[] = { 1, 2, 3, 4 }; + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Act + ElemDownloadGraphicsBufferDataOptions downloadOptions = + { + .Offset = 2, + .SizeInBytes = 2 + }; + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, &downloadOptions); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + ASSERT_EQ_MSG(resourceDataSpan.Length, 2u, "Resource dataspan length should be equals to specified SizeInBytes."); + ASSERT_EQ_MSG(resourceDataSpan.Items[0], data[2], "Resource dataspan element 0 should be equal to test data at offset 2."); +} + +UTEST(Resource, ElemDownloadGraphicsBufferData_WithTexture2D) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -516,14 +636,14 @@ UTEST(Resource, GetGraphicsResourceDataSpan_WithTexture2D) auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); // Act - auto resourceDataSpan = ElemGetGraphicsResourceDataSpan(resource); + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); // Assert ElemFreeGraphicsResource(resource, nullptr); ElemFreeGraphicsHeap(graphicsHeap); ElemFreeGraphicsDevice(graphicsDevice); - ASSERT_LOG_MESSAGE("GetGraphicsResourceDataSpan only works with graphics buffers."); + ASSERT_LOG_MESSAGE("ElemDownloadGraphicsBufferData only works with graphics buffers."); ASSERT_TRUE_MSG(resourceDataSpan.Items == nullptr, "Resource dataspan pointer should be null."); ASSERT_EQ_MSG(resourceDataSpan.Length, 0u, "Resource dataspan length should be 0."); diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index e5ec010b..8d4cf2f2 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -87,7 +87,7 @@ UTEST(Shader, DispatchCompute) TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "ShaderTests.shader", "TestCompute", 1, 1, 1, &readbackBuffer.WriteDescriptor); // Assert - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); ElemFreeCommandQueue(commandQueue); @@ -168,7 +168,7 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateFillAndCullMode) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -341,7 +341,7 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); @@ -501,7 +501,7 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateBlendState) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); - auto bufferData = ElemGetGraphicsResourceDataSpan(readbackBuffer.Buffer); + auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); TestFreeGpuTexture(renderTarget); From 7fe308d9de43b1590c5f1f2df846e787a4e2824d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 1 Jan 2025 12:56:50 +0100 Subject: [PATCH 51/93] Mem management changes --- samples/Elemental/99-Renderer/main.c | 2 - .../Apple/Graphics/MetalResource.cpp | 4 + src/Elemental/Apple/Graphics/MetalResource.h | 1 + src/Elemental/Common/Graphics/Resource.cpp | 5 + .../Common/Graphics/Vulkan/VulkanResource.cpp | 8 +- .../Common/Graphics/Vulkan/VulkanResource.h | 1 + src/Elemental/Elemental.h | 23 +- src/Elemental/ElementalLoader.c | 19 ++ .../Graphics/DirectX12GraphicsDevice.cpp | 40 ++- .../Graphics/DirectX12GraphicsDevice.h | 1 + .../Microsoft/Graphics/DirectX12Resource.cpp | 65 ++++- .../Microsoft/Graphics/DirectX12Resource.h | 8 +- .../Microsoft/Graphics/DirectX12SwapChain.cpp | 2 +- tests/GraphicsTests/GraphicsTests.h | 2 +- tests/GraphicsTests/ResourceTests.cpp | 230 +++++++++++++----- 15 files changed, 329 insertions(+), 82 deletions(-) diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index cfae5285..04eb8aca 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -80,8 +80,6 @@ void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, u applicationPayload->DepthBuffer = ElemCreateGraphicsResource(applicationPayload->DepthBufferHeap, 0, &resourceInfo); } -// TODO: Copy paste the loading scene code from the common header because it is the first sample to explain how to load a mesh - void UpdateFrameData(ApplicationPayload* applicationPayload, SampleMatrix4x4 viewProjMatrix, bool showMeshlets) { applicationPayload->FrameData.ViewProjMatrix = viewProjMatrix; diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index 03d856a2..8a6f771e 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -470,6 +470,10 @@ ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, cons return {}; } +void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) +{ +} + ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) { SystemAssert(resource != ELEM_HANDLE_NULL); diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index 7ef36b16..b281902d 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -51,6 +51,7 @@ ElemGraphicsResourceInfo MetalGetGraphicsResourceInfo(ElemGraphicsResource resou void MetalUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); +void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters); ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo MetalGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/Common/Graphics/Resource.cpp b/src/Elemental/Common/Graphics/Resource.cpp index ebf655e5..afaa83a7 100644 --- a/src/Elemental/Common/Graphics/Resource.cpp +++ b/src/Elemental/Common/Graphics/Resource.cpp @@ -56,6 +56,11 @@ ElemAPI ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource resourc DispatchReturnGraphicsFunction(DownloadGraphicsBufferData, resource, options); } +ElemAPI void ElemCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) +{ + DispatchGraphicsFunction(CopyDataToGraphicsResource, commandList, parameters); +} + ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) { DispatchReturnGraphicsFunction(CreateGraphicsResourceDescriptor, resource, usage, options); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index f9a51621..bbda8afc 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -236,9 +236,7 @@ ElemGraphicsHeap VulkanCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uin VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocateInfo.allocationSize = sizeInBytes; - // TODO: Still need to investigate this - //allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; - allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuUploadMemoryTypeIndex; + allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; if (options) { @@ -592,6 +590,10 @@ ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, con return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } +void VulkanCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) +{ +} + ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) { SystemAssert(resource != ELEM_HANDLE_NULL); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index 05424bba..c883b7f3 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -55,6 +55,7 @@ ElemGraphicsResourceInfo VulkanGetGraphicsResourceInfo(ElemGraphicsResource reso void VulkanUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); +void VulkanCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters); ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo VulkanGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 0c79140a..0794e6ca 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -384,7 +384,6 @@ typedef enum typedef enum { ElemGraphicsHeapType_Gpu = 0, - // TODO: Get rid of this? ElemGraphicsHeapType_GpuUpload = 1, ElemGraphicsHeapType_Readback = 2 } ElemGraphicsHeapType; @@ -406,7 +405,7 @@ typedef enum typedef enum { ElemGraphicsResourceType_Buffer, - ElemGraphicsResourceType_Texture2D + ElemGraphicsResourceType_Texture2D // TODO: Do we keep the distinction for 2D? Maybe just Texture is enough } ElemGraphicsResourceType; typedef enum @@ -757,6 +756,24 @@ typedef struct uint32_t SizeInBytes; } ElemDownloadGraphicsBufferDataOptions; +typedef enum +{ + ElemCopyDataSourceType_Memory = 0, + ElemCopyDataSourceType_File = 1, +} ElemCopyDataSourceType; + +typedef struct +{ + ElemGraphicsResource Resource; + uint32_t BufferOffset; + uint32_t TextureMipLevel; + ElemCopyDataSourceType SourceType; + uint32_t SourceFilePath; + uint32_t SourceFileOffset; + uint32_t SourceFileSizeInBytes; + ElemDataSpan SourceMemoryData; +} ElemCopyDataToGraphicsResourceParameters; + typedef struct { ElemGraphicsFormat Format; @@ -1118,7 +1135,7 @@ ElemAPI ElemGraphicsResourceInfo ElemGetGraphicsResourceInfo(ElemGraphicsResourc // TODO: uint64_t for offset? ElemAPI void ElemUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); ElemAPI ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); -//ElemAPI ElemFence ElemCopyDataToGraphicsResource(ElemCommandQueue commandQueue); +ElemAPI void ElemCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters); ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemAPI ElemGraphicsResourceDescriptorInfo ElemGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index 57428eb0..f3a160c6 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -63,6 +63,7 @@ typedef struct ElementalFunctions ElemGraphicsResourceInfo (*ElemGetGraphicsResourceInfo)(ElemGraphicsResource); void (*ElemUploadGraphicsBufferData)(ElemGraphicsResource, unsigned int, ElemDataSpan); ElemDataSpan (*ElemDownloadGraphicsBufferData)(ElemGraphicsResource, ElemDownloadGraphicsBufferDataOptions const *); + void (*ElemCopyDataToGraphicsResource)(ElemCommandList, ElemCopyDataToGraphicsResourceParameters const *); ElemGraphicsResourceDescriptor (*ElemCreateGraphicsResourceDescriptor)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *); ElemGraphicsResourceDescriptorInfo (*ElemGetGraphicsResourceDescriptorInfo)(ElemGraphicsResourceDescriptor); void (*ElemFreeGraphicsResourceDescriptor)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *); @@ -177,6 +178,7 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemGetGraphicsResourceInfo = (ElemGraphicsResourceInfo (*)(ElemGraphicsResource))GetElementalFunctionPointer("ElemGetGraphicsResourceInfo"); listElementalFunctions.ElemUploadGraphicsBufferData = (void (*)(ElemGraphicsResource, unsigned int, ElemDataSpan))GetElementalFunctionPointer("ElemUploadGraphicsBufferData"); listElementalFunctions.ElemDownloadGraphicsBufferData = (ElemDataSpan (*)(ElemGraphicsResource, ElemDownloadGraphicsBufferDataOptions const *))GetElementalFunctionPointer("ElemDownloadGraphicsBufferData"); + listElementalFunctions.ElemCopyDataToGraphicsResource = (void (*)(ElemCommandList, ElemCopyDataToGraphicsResourceParameters const *))GetElementalFunctionPointer("ElemCopyDataToGraphicsResource"); listElementalFunctions.ElemCreateGraphicsResourceDescriptor = (ElemGraphicsResourceDescriptor (*)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsResourceDescriptor"); listElementalFunctions.ElemGetGraphicsResourceDescriptorInfo = (ElemGraphicsResourceDescriptorInfo (*)(ElemGraphicsResourceDescriptor))GetElementalFunctionPointer("ElemGetGraphicsResourceDescriptorInfo"); listElementalFunctions.ElemFreeGraphicsResourceDescriptor = (void (*)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResourceDescriptor"); @@ -1266,6 +1268,23 @@ static inline ElemDataSpan ElemDownloadGraphicsBufferData(ElemGraphicsResource r return listElementalFunctions.ElemDownloadGraphicsBufferData(resource, options); } +static inline void ElemCopyDataToGraphicsResource(ElemCommandList commandList, ElemCopyDataToGraphicsResourceParameters const * parameters) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + return; + } + + if (!listElementalFunctions.ElemCopyDataToGraphicsResource) + { + assert(listElementalFunctions.ElemCopyDataToGraphicsResource); + return; + } + + listElementalFunctions.ElemCopyDataToGraphicsResource(commandList, parameters); +} + static inline ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, ElemGraphicsResourceDescriptorOptions const * options) { if (!LoadElementalFunctionPointers()) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index c7efca69..0f8c54b1 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -107,7 +107,8 @@ void InitDirectX12GraphicsDeviceMemory() { if (!DirectX12MemoryArena.Storage) { - DirectX12MemoryArena = SystemAllocateMemoryArena(); + // TODO: To review + DirectX12MemoryArena = SystemAllocateMemoryArena(128 * 1024 * 1024); directX12GraphicsDevicePool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_DEVICES); InitDirectX12(); @@ -298,6 +299,43 @@ ComPtr CreateDirectX12RootSignature(ComPtr return rootSignature; } +// TODO: We create an initial 256 MB buffer for now. This buffer will need to be resized if a big upload is asked. +ElemGraphicsResource CreateDirectX12TextureUploadBuffer(ComPtr graphicsDevice, uint64_t sizeInBytes) +{ + D3D12_RESOURCE_DESC bufferDescription = + { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = 0, + .Width = sizeInBytes, + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = + { + .Count = 1, + .Quality = 0 + }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE + }; + + /* + ComPtr resource; + AssertIfFailed(graphicsDevice->CreateCommittedResource2(const D3D12_HEAP_PROPERTIES *pHeapProperties, + D3D12_HEAP_FLAGS HeapFlags, + const D3D12_RESOURCE_DESC1 *pDesc, + D3D12_RESOURCE_STATES InitialResourceState, + const D3D12_CLEAR_VALUE *pOptimizedClearValue, + ID3D12ProtectedResourceSession *pProtectedSession, + const IID &riidResource, void **ppvResource) + + if (DirectX12DebugLayerEnabled && resourceInfo->DebugName) + { + resource->SetName(SystemConvertUtf8ToWideChar(stackMemoryArena, resourceInfo->DebugName).Pointer); + }*/ +} + void DirectX12SetGraphicsOptions(const ElemGraphicsOptions* options) { SystemAssert(options); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h index 112377af..0de1c892 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h @@ -23,6 +23,7 @@ struct DirectX12GraphicsDeviceData DirectX12DescriptorHeap RTVDescriptorHeap; DirectX12DescriptorHeap DSVDescriptorHeap; MemoryArena MemoryArena; + ComPtr TextureUploadBuffer; }; struct DirectX12GraphicsDeviceDataFull diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 867e8ea7..e6786aee 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -1,5 +1,6 @@ #include "DirectX12Resource.h" #include "DirectX12GraphicsDevice.h" +#include "DirectX12CommandList.h" #include "Graphics/Resource.h" #include "Graphics/ResourceDeleteQueue.h" #include "SystemDataPool.h" @@ -77,7 +78,7 @@ DXGI_FORMAT ConvertDirectX12FormatWithoutSrgbIfNeeded(DXGI_FORMAT format) } } -ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ComPtr resource, bool isPresentTexture) +ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap heap, ComPtr resource, bool isPresentTexture) { InitDirectX12ResourceMemory(); @@ -120,10 +121,11 @@ ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDev .Type = type, .DirectX12Format = resourceDesc.Format, .DirectX12Flags = resourceDesc.Flags, + .GraphicsHeap = heap, .Width = (uint32_t)resourceDesc.Width, .Height = type != ElemGraphicsResourceType_Buffer ? (uint32_t)resourceDesc.Height : 0, .MipLevels = resourceDesc.MipLevels, - .IsPresentTexture = isPresentTexture + .IsPresentTexture = isPresentTexture, }); SystemAddDataPoolItemFull(directX12GraphicsResourcePool, handle, { @@ -323,12 +325,10 @@ ElemGraphicsHeap DirectX12CreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, .DeviceObject = graphicsHeap, .SizeInBytes = sizeInBytes, .GraphicsDevice = graphicsDevice, + .HeapDescription = heapDesc, + .HeapType = heapProperties.Type }); - SystemAddDataPoolItemFull(directX12GraphicsHeapPool, handle, { - .HeapDescription = heapDesc - }); - return handle; } @@ -495,7 +495,7 @@ ElemGraphicsResource DirectX12CreateGraphicsResource(ElemGraphicsHeap graphicsHe resource->SetName(SystemConvertUtf8ToWideChar(stackMemoryArena, resourceInfo->DebugName).Pointer); } - return CreateDirectX12GraphicsResourceFromResource(graphicsHeapData->GraphicsDevice, resourceInfo->Type, resource, false); + return CreateDirectX12GraphicsResourceFromResource(graphicsHeapData->GraphicsDevice, resourceInfo->Type, graphicsHeap, resource, false); } void DirectX12FreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options) @@ -558,12 +558,21 @@ void DirectX12UploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t o auto resourceData = GetDirectX12GraphicsResourceData(resource); SystemAssert(resourceData); + auto heapData = GetDirectX12GraphicsHeapData(resourceData->GraphicsHeap); + SystemAssert(heapData); + if (resourceData->Type != ElemGraphicsResourceType_Buffer) { SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers."); return; } + if (heapData->HeapType != D3D12_HEAP_TYPE_GPU_UPLOAD) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); + return; + } + if (resourceData->CpuDataPointer == nullptr) { D3D12_RANGE readRange = { 0, 0 }; @@ -581,12 +590,26 @@ ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, auto resourceData = GetDirectX12GraphicsResourceData(resource); SystemAssert(resourceData); + auto heapData = GetDirectX12GraphicsHeapData(resourceData->GraphicsHeap); + SystemAssert(heapData); + if (resourceData->Type != ElemGraphicsResourceType_Buffer) { SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers."); return {}; } + if (heapData->HeapType != D3D12_HEAP_TYPE_CUSTOM && heapData->HeapType != D3D12_HEAP_TYPE_GPU_UPLOAD) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap. (%d)", heapData->HeapType); + return {}; + } + + if (heapData->HeapType != D3D12_HEAP_TYPE_CUSTOM) + { + SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData works faster with graphics buffers allocated in a Readback heap."); + } + if (resourceData->CpuDataPointer == nullptr) { D3D12_RANGE readRange = { 0, 0 }; @@ -613,6 +636,34 @@ ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } +void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) +{ + // TODO: Implement optimizations on the copy queue. On windows, use DirectStorage + // TODO: Implement file source + + auto stackMemoryArena = SystemGetStackMemoryArena(); + + SystemAssert(commandList != ELEM_HANDLE_NULL); + + SystemAssert(parameters); + SystemAssert(parameters->Resource != ELEM_HANDLE_NULL); + + auto commandListData = GetDirectX12CommandListData(commandList); + SystemAssert(commandListData); + + auto resourceData = GetDirectX12GraphicsResourceData(parameters->Resource); + SystemAssert(resourceData); + + if (resourceData->Type == ElemGraphicsResourceType_Buffer) + { + //commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject, parameters->BufferOffset, ID3D12Resource *pSrcBuffer, parameters., UINT64 NumBytes) + } + else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + //commandListData->DeviceObject->CopyTextureRegion(const D3D12_TEXTURE_COPY_LOCATION *pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION *pSrc, const D3D12_BOX *pSrcBox) + } +} + ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) { SystemAssert(resource != ELEM_HANDLE_NULL); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index 536e8f5e..6dceea29 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -7,12 +7,14 @@ struct DirectX12GraphicsHeapData ComPtr DeviceObject; uint64_t SizeInBytes; ElemGraphicsDevice GraphicsDevice; + D3D12_HEAP_DESC HeapDescription; + D3D12_HEAP_TYPE HeapType; }; // TODO: To review we don't use it!!! struct DirectX12GraphicsHeapDataFull { - D3D12_HEAP_DESC HeapDescription; + uint32_t reserved; }; struct DirectX12GraphicsResourceData @@ -23,6 +25,7 @@ struct DirectX12GraphicsResourceData ElemGraphicsResourceType Type; DXGI_FORMAT DirectX12Format; D3D12_RESOURCE_FLAGS DirectX12Flags; + ElemGraphicsHeap GraphicsHeap; uint32_t Width; uint32_t Height; uint32_t MipLevels; @@ -41,7 +44,7 @@ DirectX12GraphicsHeapDataFull* GetDirectX12GraphicsHeapDataFull(ElemGraphicsHeap DirectX12GraphicsResourceData* GetDirectX12GraphicsResourceData(ElemGraphicsResource resource); DirectX12GraphicsResourceDataFull* GetDirectX12GraphicsResourceDataFull(ElemGraphicsResource resource); -ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ComPtr resource, bool isPresentTexture); +ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap heap, ComPtr resource, bool isPresentTexture); DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format); bool CheckDirectX12DepthStencilFormat(ElemGraphicsFormat format); @@ -57,6 +60,7 @@ ElemGraphicsResourceInfo DirectX12GetGraphicsResourceInfo(ElemGraphicsResource r void DirectX12UploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data); ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options); +void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters); ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options); ElemGraphicsResourceDescriptorInfo DirectX12GetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index cecf1215..5a5e06ff 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -52,7 +52,7 @@ void CreateDirectX12SwapChainRenderTargetViews(ElemSwapChain swapChain) ComPtr backBuffer; AssertIfFailed(swapChainData->DeviceObject->GetBuffer(i, IID_PPV_ARGS(backBuffer.GetAddressOf()))); - swapChainData->BackBufferTextures[i] = CreateDirectX12GraphicsResourceFromResource(swapChainDataFull->GraphicsDevice, ElemGraphicsResourceType_Texture2D, backBuffer, true); + swapChainData->BackBufferTextures[i] = CreateDirectX12GraphicsResourceFromResource(swapChainDataFull->GraphicsDevice, ElemGraphicsResourceType_Texture2D, ELEM_HANDLE_NULL, backBuffer, true); } } diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index cc51ba43..3cef28bf 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -181,7 +181,7 @@ ElemShaderLibrary TestOpenShader(ElemGraphicsDevice graphicsDevice, const char* ElemPipelineState TestOpenComputeShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* function); ElemPipelineState TestOpenMeshShader(ElemGraphicsDevice graphicsDevice, const char* shader, const char* meshShaderFunction, const char* pixelShaderFunction, const ElemGraphicsPipelineStateParameters* baseParameters); -TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_Gpu); +TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t sizeInBytes, ElemGraphicsHeapType heapType = ElemGraphicsHeapType_GpuUpload); void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer); TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage); diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index a45965fd..576ba39f 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -479,7 +479,85 @@ UTEST(Resource, CreateTexture2DResourceInfo_RenderTargetWrite) ASSERT_STREQ_MSG(resourceInfo.DebugName, "TestTexture2D", "Debug name should match the creation usage."); } -UTEST(Resource, ElemUploadGraphicsBufferData_WithBuffer) +UTEST(Resource, FreeGraphicsResource) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + ElemFreeGraphicsResource(resource, nullptr); + + // Assert + auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(afterFreeResourceInfo.Width, 0u, "Width should be equals to 0."); +} + +UTEST(Resource, FreeGraphicsResource_WithFenceNotExecuted) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + ElemFence fence = { .CommandQueue = commandQueue, .FenceValue = UINT64_MAX }; + ElemFreeGraphicsResourceOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; + + // Act + ElemFreeGraphicsResource(resource, &options); + + // Assert + ElemProcessGraphicsResourceDeleteQueue(); + auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); + + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(afterFreeResourceInfo.Width, resourceInfo.Width, "Width should be equals to creation info."); +} + +UTEST(Resource, FreeGraphicsResource_WithFenceExecuted) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto commandList = ElemGetCommandList(commandQueue, nullptr); + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + + ElemFreeGraphicsResourceOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; + + // Act + ElemFreeGraphicsResource(resource, &options); + + // Assert + ElemWaitForFenceOnCpu(fence); + ElemProcessGraphicsResourceDeleteQueue(); + + auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(afterFreeResourceInfo.Width, 0u, "Width should be equals to 0."); +} + +UTEST(Resource, UploadGraphicsBufferData_WithBuffer) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -506,7 +584,34 @@ UTEST(Resource, ElemUploadGraphicsBufferData_WithBuffer) ASSERT_LOG_NOERROR(); } -UTEST(Resource, ElemUploadGraphicsBufferData_WithBufferOffsetSize) +UTEST(Resource, UploadGraphicsBufferData_WithNoGpuUploadHeap) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Gpu + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); +} + +UTEST(Resource, UploadGraphicsBufferData_WithBufferOffsetSize) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -541,7 +646,7 @@ UTEST(Resource, ElemUploadGraphicsBufferData_WithBufferOffsetSize) ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); } -UTEST(Resource, ElemUploadGraphicsBufferData_WithTexture2D) +UTEST(Resource, UploadGraphicsBufferData_WithTexture2D) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -561,7 +666,7 @@ UTEST(Resource, ElemUploadGraphicsBufferData_WithTexture2D) ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers."); } -UTEST(Resource, ElemDownloadGraphicsBufferData_WithBuffer) +UTEST(Resource, DownloadGraphicsBufferData_WithBuffer) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -589,7 +694,32 @@ UTEST(Resource, ElemDownloadGraphicsBufferData_WithBuffer) ASSERT_EQ_MSG(resourceDataSpan.Length, 1024u, "Resource dataspan length should be equals to buffer size."); } -UTEST(Resource, ElemDownloadGraphicsBufferData_WithBufferOffsetAndSize) +UTEST(Resource, DownloadGraphicsBufferData_WithNoReadbackHeap) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Gpu + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("DownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap."); +} + +UTEST(Resource, DownloadGraphicsBufferData_WithBufferOffsetAndSize) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -627,7 +757,7 @@ UTEST(Resource, ElemDownloadGraphicsBufferData_WithBufferOffsetAndSize) ASSERT_EQ_MSG(resourceDataSpan.Items[0], data[2], "Resource dataspan element 0 should be equal to test data at offset 2."); } -UTEST(Resource, ElemDownloadGraphicsBufferData_WithTexture2D) +UTEST(Resource, DownloadGraphicsBufferData_WithTexture2D) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); @@ -649,82 +779,58 @@ UTEST(Resource, ElemDownloadGraphicsBufferData_WithTexture2D) ASSERT_EQ_MSG(resourceDataSpan.Length, 0u, "Resource dataspan length should be 0."); } -UTEST(Resource, FreeGraphicsResource) +UTEST(Resource, CopyDataToGraphicsResource_WithBuffer) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - // Act - ElemFreeGraphicsResource(resource, nullptr); - - // Assert - auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - ASSERT_EQ_MSG(afterFreeResourceInfo.Width, 0u, "Width should be equals to 0."); -} + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; -UTEST(Resource, FreeGraphicsResource_WithFenceNotExecuted) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - ElemFence fence = { .CommandQueue = commandQueue, .FenceValue = UINT64_MAX }; - ElemFreeGraphicsResourceOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; - // Act - ElemFreeGraphicsResource(resource, &options); - - // Assert - ElemProcessGraphicsResourceDeleteQueue(); - auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); - - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - ASSERT_EQ_MSG(afterFreeResourceInfo.Width, resourceInfo.Width, "Width should be equals to creation info."); -} + uint8_t data[] = { 1, 2, 3, 4 }; -UTEST(Resource, FreeGraphicsResource_WithFenceExecuted) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - ElemCommitCommandList(commandList); - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - - ElemFreeGraphicsResourceOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; // Act - ElemFreeGraphicsResource(resource, &options); + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = 2, + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = data, .Length = 2 } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); // Assert + ElemCommitCommandList(commandList); + + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - ElemProcessGraphicsResourceDeleteQueue(); - auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); - ElemFreeGraphicsHeap(graphicsHeap); + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - ASSERT_EQ_MSG(afterFreeResourceInfo.Width, 0u, "Width should be equals to 0."); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + + ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); + ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); } UTEST(Resource, CreateGraphicsResourceDescriptor_ReadWithBuffer) From 60c928f0c4c5ae5232e4dbbdd675368e623446b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 3 Jan 2025 09:13:06 +0100 Subject: [PATCH 52/93] WIP in upload buffer management --- .../Apple/Graphics/MetalGraphicsDevice.cpp | 3 +- .../Apple/Graphics/MetalResource.cpp | 10 +- src/Elemental/Apple/Graphics/MetalResource.h | 2 +- src/Elemental/Common/Graphics/Resource.cpp | 4 +- .../Common/Graphics/UploadBufferPool.cpp | 84 ++++++++ .../Common/Graphics/UploadBufferPool.h | 45 +++++ .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 3 +- .../Graphics/Vulkan/VulkanGraphicsDevice.h | 5 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 16 +- .../Common/Graphics/Vulkan/VulkanResource.h | 2 +- .../Graphics/Vulkan/VulkanSwapChain.cpp | 2 +- src/Elemental/Common/SystemDataPool.cpp | 8 +- src/Elemental/Common/SystemMemory.cpp | 2 +- src/Elemental/Elemental.h | 17 +- src/Elemental/ElementalLoader.c | 8 +- .../Graphics/DirectX12GraphicsDevice.cpp | 65 +++---- .../Graphics/DirectX12GraphicsDevice.h | 10 +- .../Microsoft/Graphics/DirectX12Resource.cpp | 153 ++++++++++++++- .../Microsoft/Graphics/DirectX12Resource.h | 2 +- .../Microsoft/Graphics/DirectX12SwapChain.cpp | 2 +- src/Elemental/Microsoft/UnityBuild.cpp | 1 + src/Elemental/Microsoft/Win32Application.cpp | 26 +++ tests/GraphicsTests/ResourceTests.cpp | 180 +++++++++++++++++- 23 files changed, 558 insertions(+), 92 deletions(-) create mode 100644 src/Elemental/Common/Graphics/UploadBufferPool.cpp create mode 100644 src/Elemental/Common/Graphics/UploadBufferPool.h diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp index c37b7d45..8cc1b091 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp @@ -113,7 +113,8 @@ void InitMetalGraphicsDeviceMemory() { if (!MetalGraphicsMemoryArena.Storage) { - MetalGraphicsMemoryArena = SystemAllocateMemoryArena(); + // TODO: To Review + MetalGraphicsMemoryArena = SystemAllocateMemoryArena(128 * 1024 * 1024); metalGraphicsDevicePool = SystemCreateDataPool(MetalGraphicsMemoryArena, METAL_MAXDEVICES); InitMetal(); diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index 8a6f771e..8a8960ef 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -21,7 +21,7 @@ void InitMetalResourceMemory() metalResourcePool = SystemCreateDataPool(MetalGraphicsMemoryArena, METAL_MAX_RESOURCES); // TODO: This should be part of the graphics device data - metalResourceDescriptorInfos = SystemPushArray(MetalGraphicsMemoryArena, METAL_MAX_RESOURCES); + metalResourceDescriptorInfos = SystemPushArray(MetalGraphicsMemoryArena, METAL_MAX_RESOURCES, AllocationState_Reserved); } } @@ -511,8 +511,14 @@ ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphic handle = CreateMetalArgumentBufferHandleForBuffer(graphicsDeviceData->ResourceArgumentBuffer, (MTL::Buffer*)resourceData->DeviceObject.get(), resourceData->Width); } + if ((handle % 1000) == 0) + { + SystemPlatformCommitMemory(&metalResourceDescriptorInfos[descriptorHandle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + } + metalResourceDescriptorInfos[handle].Resource = resource; metalResourceDescriptorInfos[handle].Usage = usage; + return handle; } @@ -544,7 +550,7 @@ void MetalFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descript metalResourceDescriptorInfos[descriptor].Resource = ELEM_HANDLE_NULL; } -void MetalProcessGraphicsResourceDeleteQueue() +void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { ProcessResourceDeleteQueue(); } diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index b281902d..b12558ee 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -57,6 +57,6 @@ ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphic ElemGraphicsResourceDescriptorInfo MetalGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); void MetalFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descriptor, const ElemFreeGraphicsResourceDescriptorOptions* options); -void MetalProcessGraphicsResourceDeleteQueue(); +void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture); diff --git a/src/Elemental/Common/Graphics/Resource.cpp b/src/Elemental/Common/Graphics/Resource.cpp index afaa83a7..4e89664f 100644 --- a/src/Elemental/Common/Graphics/Resource.cpp +++ b/src/Elemental/Common/Graphics/Resource.cpp @@ -81,7 +81,7 @@ ElemAPI void ElemGraphicsResourceBarrier(ElemCommandList commandList, ElemGraphi DispatchGraphicsFunction(GraphicsResourceBarrier, commandList, descriptor, options); } -ElemAPI void ElemProcessGraphicsResourceDeleteQueue() +ElemAPI void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { - DispatchGraphicsFunction(ProcessGraphicsResourceDeleteQueue); + DispatchGraphicsFunction(ProcessGraphicsResourceDeleteQueue, graphicsDevice); } diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp new file mode 100644 index 00000000..3e41f0a2 --- /dev/null +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -0,0 +1,84 @@ +#include "UploadBufferPool.h" +#include "SystemFunctions.h" + +template +UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t sizeInBytes) +{ + if (uploadBufferPool->Generation != generation) + { + uploadBufferPool->CurrentUploadBuffer = nullptr; + uploadBufferPool->Generation = generation; + } + + if (uploadBufferPool->CurrentUploadBuffer != nullptr) + { + if (uploadBufferPool->CurrentUploadBuffer->CurrentOffset + sizeInBytes > uploadBufferPool->CurrentUploadBuffer->SizeInBytes) + { + uploadBufferPool->CurrentUploadBuffer = nullptr; + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Too big"); + } + } + + if (uploadBufferPool->CurrentUploadBuffer == nullptr) + { + uploadBufferPool->CurrentUploadBuffer = &uploadBufferPool->UploadBuffers[uploadBufferPool->CurrentUploadBufferIndex]; + + auto currentUploadBuffer = uploadBufferPool->CurrentUploadBuffer; + uploadBufferPool->CurrentUploadBufferIndex = uploadBufferPool->CurrentUploadBufferIndex + 1 % MAX_UPLOAD_BUFFERS; + + // TODO: Split that into a NeedBufferCreation and NeedBufferDelete + currentUploadBuffer->IsResetNeeded = true; + currentUploadBuffer->CurrentOffset = 0u; + + uint64_t alignedSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512llu * 1024u * 1024u); + + if (alignedSize == 0) + { + // TODO: put that in a constant + alignedSize = 16u * 1024u * 1024u; + } + + while (alignedSize < sizeInBytes) + { + alignedSize <<= 1; + } + + uploadBufferPool->LastBufferSize = alignedSize; + currentUploadBuffer->SizeInBytes = alignedSize; + } + + // TODO: Check alignment + auto offset = uploadBufferPool->CurrentUploadBuffer->CurrentOffset; + uploadBufferPool->CurrentUploadBuffer->CurrentOffset += sizeInBytes; + + return + { + .PoolItem = uploadBufferPool->CurrentUploadBuffer, + .Offset = offset + }; +} + +/* +template +CommandListPoolItem* GetCommandListPoolItem(CommandAllocatorPoolItem* commandAllocatorPoolItem) +{ + auto commandListPoolItem = &commandAllocatorPoolItem->CommandListPoolItems[commandAllocatorPoolItem->CurrentCommandListIndex]; + SystemAssert (!commandListPoolItem->IsInUse); + + commandAllocatorPoolItem->CurrentCommandListIndex = (commandAllocatorPoolItem->CurrentCommandListIndex + 1) % MAX_COMMANDLIST; + + commandListPoolItem->IsInUse = true; + return commandListPoolItem; +} + +template +void ReleaseCommandListPoolItem(CommandListPoolItem* commandListPoolItem) +{ + commandListPoolItem->IsInUse = false; +} + +template +void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence) +{ + commandAllocatorPoolItem->Fence = fence; +}*/ diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.h b/src/Elemental/Common/Graphics/UploadBufferPool.h new file mode 100644 index 00000000..a52fbaeb --- /dev/null +++ b/src/Elemental/Common/Graphics/UploadBufferPool.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Elemental.h" + +#define MAX_UPLOAD_BUFFERS 10 + +template +struct UploadBufferPoolItem +{ + T Buffer; + uint64_t SizeInBytes; + ElemFence Fence; + bool IsResetNeeded; + uint64_t CurrentOffset; + uint8_t* CpuPointer; +}; + +template +struct UploadBufferMemory +{ + UploadBufferPoolItem* PoolItem; + uint64_t Offset; +}; + +template +struct UploadBufferDevicePool +{ + UploadBufferPoolItem UploadBuffers[MAX_UPLOAD_BUFFERS]; + uint32_t CurrentUploadBufferIndex; + UploadBufferPoolItem* CurrentUploadBuffer; + uint64_t LastBufferSize; + uint64_t Generation; + bool IsInited; +}; + +template +UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t sizeInBytes); + +/* +template +void ReleaseCommandListPoolItem(CommandListPoolItem* commandListPoolItem); + +template +void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence); +*/ diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 70f835f3..598b2e13 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -178,7 +178,8 @@ void InitVulkanGraphicsDeviceMemory() { if (!VulkanGraphicsMemoryArena.Storage) { - VulkanGraphicsMemoryArena = SystemAllocateMemoryArena(); + // TODO: To Review + VulkanGraphicsMemoryArena = SystemAllocateMemoryArena(256 * 1024 * 1024); vulkanGraphicsDevicePool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_DEVICES); InitVulkan(); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h index 8bafbe7a..6efa9d51 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h @@ -11,7 +11,10 @@ #include "volk.h" #define VULKAN_MAX_DEVICES 10u -#define VULKAN_MAX_RESOURCES 400000 + +// TODO: Investigate why we cannot bump this to 1 million +//#define VULKAN_MAX_RESOURCES 400000 +#define VULKAN_MAX_RESOURCES 1000000 struct VulkanDescriptorHeapStorage; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index bbda8afc..abe22d30 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -23,9 +23,11 @@ void InitVulkanResourceMemory() { vulkanGraphicsHeapPool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_GRAPHICSHEAP); vulkanGraphicsResourcePool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); - vulkanResourceDescriptorInfos = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); - vulkanResourceDescriptorImageViews = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES); - vulkanReadBackMemoryArena = SystemAllocateMemoryArena(); + + vulkanResourceDescriptorInfos = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES, AllocationState_Reserved); + vulkanResourceDescriptorImageViews = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES, AllocationState_Reserved); + + vulkanReadBackMemoryArena = SystemAllocateMemoryArena(32 * 1024 * 1024); } } @@ -636,6 +638,12 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi auto descriptorHeap = graphicsDeviceData->ResourceDescriptorHeap; auto descriptorHandle = CreateVulkanDescriptorHandle(descriptorHeap); + if ((descriptorHandle % 1000) == 0) + { + SystemPlatformCommitMemory(&vulkanResourceDescriptorInfos[descriptorHandle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + SystemPlatformCommitMemory(&vulkanResourceDescriptorImageViews[descriptorHandle], 1000 * sizeof(VkImageView)); + } + VkWriteDescriptorSet descriptor = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; descriptor.dstSet = descriptorHeap.Storage->DescriptorSet; descriptor.dstBinding = 0; @@ -729,7 +737,7 @@ void VulkanFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descrip vulkanResourceDescriptorInfos[descriptor].Resource = ELEM_HANDLE_NULL; } -void VulkanProcessGraphicsResourceDeleteQueue() +void VulkanProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { ProcessResourceDeleteQueue(); SystemClearMemoryArena(vulkanReadBackMemoryArena); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index c883b7f3..dba23cdc 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -61,6 +61,6 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi ElemGraphicsResourceDescriptorInfo VulkanGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); void VulkanFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descriptor, const ElemFreeGraphicsResourceDescriptorOptions* options); -void VulkanProcessGraphicsResourceDeleteQueue(void); +void VulkanProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); void VulkanGraphicsResourceBarrier(ElemCommandList commandList, ElemGraphicsResourceDescriptor descriptor, const ElemGraphicsResourceBarrierOptions* options); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 23e53c28..99a7ba17 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -518,7 +518,7 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) AssertIfFailed(vkQueuePresentKHR(commandQueueData->DeviceObject, &presentInfo)); VulkanResetCommandAllocation(swapChainData->GraphicsDevice); - VulkanProcessGraphicsResourceDeleteQueue(); + VulkanProcessGraphicsResourceDeleteQueue(swapChainData->GraphicsDevice); swapChainData->PresentId++; } diff --git a/src/Elemental/Common/SystemDataPool.cpp b/src/Elemental/Common/SystemDataPool.cpp index 9e1e2d08..4a374128 100644 --- a/src/Elemental/Common/SystemDataPool.cpp +++ b/src/Elemental/Common/SystemDataPool.cpp @@ -2,8 +2,6 @@ #include "SystemFunctions.h" #include "SystemLogging.h" -// TODO: Implement commit memory for big data pools - #define SYSTEM_DATAPOOL_INDEX_EMPTY UINT32_MAX template @@ -50,6 +48,7 @@ SystemDataPool SystemCreateDataPool(MemoryArena memoryArena, size_t ma { auto storage = SystemPushStructZero>(memoryArena); storage->MemoryArena = memoryArena; + storage->Data = SystemPushArray>(memoryArena, maxItems, AllocationState_Reserved); if (!IsTypeEmpty()) @@ -93,11 +92,12 @@ ElemHandle SystemAddDataPoolItem(SystemDataPool dataPool, T data) } index = SystemAtomicAdd(storage->CurrentIndex, 1); - SystemCommitMemory>(storage->MemoryArena, storage->Data.Slice(index), true); + + SystemCommitMemory>(storage->MemoryArena, storage->Data.Slice(index, 1000), true); if (!IsTypeEmpty()) { - SystemCommitMemory(storage->MemoryArena, storage->DataFull.Slice(index), true); + SystemCommitMemory(storage->MemoryArena, storage->DataFull.Slice(index, 1000), true); } } diff --git a/src/Elemental/Common/SystemMemory.cpp b/src/Elemental/Common/SystemMemory.cpp index 8367b6d2..7753cf3b 100644 --- a/src/Elemental/Common/SystemMemory.cpp +++ b/src/Elemental/Common/SystemMemory.cpp @@ -431,7 +431,7 @@ void* SystemPushMemory(MemoryArena memoryArena, size_t sizeInBytes, AllocationSt if (allocatedSize + sizeInBytes > storage->SizeInBytes) { - SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot push to memory arena with: %u (Allocated size is: %u, Max size is: %u)", (uint32_t)sizeInBytes, (uint32_t)allocatedSize, (uint32_t)storage->SizeInBytes); + SystemLogErrorMessage(ElemLogMessageCategory_Memory, "Cannot push to memory arena with: %d (Allocated size is: %d, Max size is: %d)", (uint32_t)sizeInBytes, (uint32_t)allocatedSize, (uint32_t)storage->SizeInBytes); return nullptr; } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 0794e6ca..089abdae 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -363,13 +363,6 @@ typedef enum //ElemCommandQueueType_IO = 2 } ElemCommandQueueType; -// TODO: Not needed? -typedef enum -{ - ElemIOCommandType_File = 0, - ElemIOCommandType_Memory = 1 -} ElemIOCommandType; - /** * Enumerates swap chain formats. */ @@ -384,7 +377,7 @@ typedef enum typedef enum { ElemGraphicsHeapType_Gpu = 0, - ElemGraphicsHeapType_GpuUpload = 1, + ElemGraphicsHeapType_GpuUpload = 1, // TODO: Still not convinced about that one. should we let GPU be gpu upload by default? ElemGraphicsHeapType_Readback = 2 } ElemGraphicsHeapType; @@ -639,12 +632,6 @@ typedef struct ElemFenceSpan FencesToWait; } ElemExecuteCommandListOptions; -// TODO: Not needed? -typedef struct -{ - ElemIOCommandType IOCommandType; -} ElemIOCommandParameters; - /** * Options for configuring a swap chain. */ @@ -1141,7 +1128,7 @@ ElemAPI ElemGraphicsResourceDescriptor ElemCreateGraphicsResourceDescriptor(Elem ElemAPI ElemGraphicsResourceDescriptorInfo ElemGetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); ElemAPI void ElemFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descriptor, const ElemFreeGraphicsResourceDescriptorOptions* options); -ElemAPI void ElemProcessGraphicsResourceDeleteQueue(void); +ElemAPI void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); /** * Creates a shader library from provided binary data, allowing shaders to be loaded and used by graphics pipeline states. diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index f3a160c6..ffd42437 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -67,7 +67,7 @@ typedef struct ElementalFunctions ElemGraphicsResourceDescriptor (*ElemCreateGraphicsResourceDescriptor)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *); ElemGraphicsResourceDescriptorInfo (*ElemGetGraphicsResourceDescriptorInfo)(ElemGraphicsResourceDescriptor); void (*ElemFreeGraphicsResourceDescriptor)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *); - void (*ElemProcessGraphicsResourceDeleteQueue)(void); + void (*ElemProcessGraphicsResourceDeleteQueue)(ElemGraphicsDevice); ElemShaderLibrary (*ElemCreateShaderLibrary)(ElemGraphicsDevice, ElemDataSpan); void (*ElemFreeShaderLibrary)(ElemShaderLibrary); ElemPipelineState (*ElemCompileGraphicsPipelineState)(ElemGraphicsDevice, ElemGraphicsPipelineStateParameters const *); @@ -182,7 +182,7 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemCreateGraphicsResourceDescriptor = (ElemGraphicsResourceDescriptor (*)(ElemGraphicsResource, ElemGraphicsResourceDescriptorUsage, ElemGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsResourceDescriptor"); listElementalFunctions.ElemGetGraphicsResourceDescriptorInfo = (ElemGraphicsResourceDescriptorInfo (*)(ElemGraphicsResourceDescriptor))GetElementalFunctionPointer("ElemGetGraphicsResourceDescriptorInfo"); listElementalFunctions.ElemFreeGraphicsResourceDescriptor = (void (*)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResourceDescriptor"); - listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue = (void (*)(void))GetElementalFunctionPointer("ElemProcessGraphicsResourceDeleteQueue"); + listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue = (void (*)(ElemGraphicsDevice))GetElementalFunctionPointer("ElemProcessGraphicsResourceDeleteQueue"); listElementalFunctions.ElemCreateShaderLibrary = (ElemShaderLibrary (*)(ElemGraphicsDevice, ElemDataSpan))GetElementalFunctionPointer("ElemCreateShaderLibrary"); listElementalFunctions.ElemFreeShaderLibrary = (void (*)(ElemShaderLibrary))GetElementalFunctionPointer("ElemFreeShaderLibrary"); listElementalFunctions.ElemCompileGraphicsPipelineState = (ElemPipelineState (*)(ElemGraphicsDevice, ElemGraphicsPipelineStateParameters const *))GetElementalFunctionPointer("ElemCompileGraphicsPipelineState"); @@ -1364,7 +1364,7 @@ static inline void ElemFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescri listElementalFunctions.ElemFreeGraphicsResourceDescriptor(descriptor, options); } -static inline void ElemProcessGraphicsResourceDeleteQueue(void) +static inline void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { if (!LoadElementalFunctionPointers()) { @@ -1378,7 +1378,7 @@ static inline void ElemProcessGraphicsResourceDeleteQueue(void) return; } - listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue(); + listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); } static inline ElemShaderLibrary ElemCreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index 0f8c54b1..f8c26375 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -299,43 +299,6 @@ ComPtr CreateDirectX12RootSignature(ComPtr return rootSignature; } -// TODO: We create an initial 256 MB buffer for now. This buffer will need to be resized if a big upload is asked. -ElemGraphicsResource CreateDirectX12TextureUploadBuffer(ComPtr graphicsDevice, uint64_t sizeInBytes) -{ - D3D12_RESOURCE_DESC bufferDescription = - { - .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, - .Alignment = 0, - .Width = sizeInBytes, - .Height = 1, - .DepthOrArraySize = 1, - .MipLevels = 1, - .Format = DXGI_FORMAT_UNKNOWN, - .SampleDesc = - { - .Count = 1, - .Quality = 0 - }, - .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, - .Flags = D3D12_RESOURCE_FLAG_NONE - }; - - /* - ComPtr resource; - AssertIfFailed(graphicsDevice->CreateCommittedResource2(const D3D12_HEAP_PROPERTIES *pHeapProperties, - D3D12_HEAP_FLAGS HeapFlags, - const D3D12_RESOURCE_DESC1 *pDesc, - D3D12_RESOURCE_STATES InitialResourceState, - const D3D12_CLEAR_VALUE *pOptimizedClearValue, - ID3D12ProtectedResourceSession *pProtectedSession, - const IID &riidResource, void **ppvResource) - - if (DirectX12DebugLayerEnabled && resourceInfo->DebugName) - { - resource->SetName(SystemConvertUtf8ToWideChar(stackMemoryArena, resourceInfo->DebugName).Pointer); - }*/ -} - void DirectX12SetGraphicsOptions(const ElemGraphicsOptions* options) { SystemAssert(options); @@ -447,13 +410,16 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions auto dsvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); auto rootSignature = CreateDirectX12RootSignature(device); + auto uploadBuffers = SystemPushArray>*>(DirectX12MemoryArena, MAX_UPLOAD_BUFFERS); + auto handle = SystemAddDataPoolItem(directX12GraphicsDevicePool, { .Device = device, .RootSignature = rootSignature, .ResourceDescriptorHeap = resourceDescriptorHeap, .RTVDescriptorHeap = rtvDescriptorHeap, .DSVDescriptorHeap = dsvDescriptorHeap, - .MemoryArena = memoryArena + .MemoryArena = memoryArena, + .UploadBufferPools = uploadBuffers }); SystemAddDataPoolItemFull(directX12GraphicsDevicePool, handle, { @@ -475,9 +441,32 @@ void DirectX12FreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) auto graphicsDeviceDataFull = GetDirectX12GraphicsDeviceDataFull(graphicsDevice); SystemAssert(graphicsDeviceDataFull); + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + for (uint32_t j = 0; j < MAX_UPLOAD_BUFFERS; j++) + { + auto uploadBuffer = &bufferPool->UploadBuffers[j]; + + if (uploadBuffer && uploadBuffer->Buffer) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Deleting upload buffer"); + uploadBuffer->Buffer.Reset(); + *uploadBuffer = {}; + } + } + + *bufferPool = {}; + } + } + FreeDirectX12DescriptorHeap(graphicsDeviceData->ResourceDescriptorHeap); FreeDirectX12DescriptorHeap(graphicsDeviceData->RTVDescriptorHeap); FreeDirectX12DescriptorHeap(graphicsDeviceData->DSVDescriptorHeap); + graphicsDeviceData->Device.Reset(); graphicsDeviceData->RootSignature.Reset(); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h index 0de1c892..7a9a04f8 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h @@ -1,11 +1,13 @@ #pragma once #include "Elemental.h" +#include "Graphics/UploadBufferPool.h" #include "SystemMemory.h" #define DIRECTX12_MAX_DEVICES 10u -//#define DIRECTX12_MAX_RESOURCES 1000000 -#define DIRECTX12_MAX_RESOURCES 500000 + +// TODO: Put this value a a config? +#define DIRECTX12_MAX_RESOURCES 1000000 struct DirectX12DescriptorHeapStorage; @@ -19,11 +21,13 @@ struct DirectX12GraphicsDeviceData ComPtr Device; ComPtr RootSignature; uint64_t CommandAllocationGeneration; + uint64_t UploadBufferGeneration; DirectX12DescriptorHeap ResourceDescriptorHeap; DirectX12DescriptorHeap RTVDescriptorHeap; DirectX12DescriptorHeap DSVDescriptorHeap; MemoryArena MemoryArena; - ComPtr TextureUploadBuffer; + Span>*> UploadBufferPools; + uint32_t CurrentUploadBufferPoolIndex; }; struct DirectX12GraphicsDeviceDataFull diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index e6786aee..d2d54c4b 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -3,6 +3,7 @@ #include "DirectX12CommandList.h" #include "Graphics/Resource.h" #include "Graphics/ResourceDeleteQueue.h" +#include "Graphics/UploadBufferPool.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" @@ -12,6 +13,8 @@ SystemDataPool directX12GraphicsHeapPool; SystemDataPool directX12GraphicsResourcePool; +thread_local UploadBufferDevicePool> threadDirectX12UploadBufferPools[DIRECTX12_MAX_DEVICES]; + // TODO: This descriptor infos should be linked to the graphics device like the resource desc heaps Span directX12ResourceDescriptorInfos; MemoryArena directX12ReadBackMemoryArena; @@ -22,9 +25,13 @@ void InitDirectX12ResourceMemory() { directX12GraphicsHeapPool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_GRAPHICSHEAP); directX12GraphicsResourcePool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES); - directX12ResourceDescriptorInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES); - directX12ReadBackMemoryArena = SystemAllocateMemoryArena(); + // TODO: That push array cause a massive memory increase + // TODO: We can push the array with reserved memory but we need to be carreful to commit the memory + directX12ResourceDescriptorInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES, AllocationState_Reserved); + + // TODO: Allow to increase the size as a parameter + directX12ReadBackMemoryArena = SystemAllocateMemoryArena(32 * 1024 * 1024); } } @@ -629,18 +636,102 @@ ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, } } - auto stackMemoryArena = SystemGetStackMemoryArena(); auto downloadedData = SystemPushArray(directX12ReadBackMemoryArena, sizeInBytes); memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } +// TODO: We create an initial 256 MB buffer for now. This buffer will need to be resized if a big upload is asked. +ComPtr CreateDirectX12UploadBuffer(ComPtr graphicsDevice, uint64_t sizeInBytes) +{ + D3D12_RESOURCE_DESC bufferDescription = + { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = 0, + .Width = sizeInBytes, + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = + { + .Count = 1, + .Quality = 0 + }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE + }; + + D3D12_HEAP_PROPERTIES heapProperties = { .Type = D3D12_HEAP_TYPE_UPLOAD }; + + ComPtr resource; + AssertIfFailed(graphicsDevice->CreateCommittedResource1(&heapProperties, + D3D12_HEAP_FLAG_NONE, + &bufferDescription, + D3D12_RESOURCE_STATE_COMMON, + nullptr, + nullptr, + IID_PPV_ARGS(resource.GetAddressOf()))); + + resource->SetName(L"ElementalUploadBuffer"); + + return resource; +} + +UploadBufferMemory> GetUploadBuffer(ElemCommandList commandList, uint64_t sizeInBytes) +{ + auto commandListData = GetDirectX12CommandListData(commandList); + SystemAssert(commandListData); + + auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(commandListData->GraphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(commandListData->GraphicsDevice); + auto uploadBufferPool = &threadDirectX12UploadBufferPools[graphicsIdUnpacked.Index]; + + if (!uploadBufferPool->IsInited) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init pool"); + + auto poolIndex = SystemAtomicAdd(graphicsDeviceData->CurrentUploadBufferPoolIndex, 1); + graphicsDeviceData->UploadBufferPools[poolIndex] = uploadBufferPool; + uploadBufferPool->IsInited = true; + } + + auto uploadBuffer = GetUploadBufferPoolItem(uploadBufferPool, graphicsDeviceData->UploadBufferGeneration, sizeInBytes); + + if (uploadBuffer.PoolItem->IsResetNeeded) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to create upload buffer with size: %d...", uploadBuffer.PoolItem->SizeInBytes); + + if (uploadBuffer.PoolItem->Fence.FenceValue > 0) + { + DirectX12WaitForFenceOnCpu(uploadBuffer.PoolItem->Fence); + } + + // TODO: We need another flag to know if we need to delete + if (uploadBuffer.PoolItem->Buffer != nullptr) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to delete buffer"); + uploadBuffer.PoolItem->Buffer.Reset(); + } + + uploadBuffer.PoolItem->Buffer = CreateDirectX12UploadBuffer(graphicsDeviceData->Device, uploadBuffer.PoolItem->SizeInBytes); + + D3D12_RANGE readRange = { 0, 0 }; + uploadBuffer.PoolItem->Buffer->Map(0, &readRange, (void**)&uploadBuffer.PoolItem->CpuPointer); + + uploadBuffer.PoolItem->IsResetNeeded = false; + } + + return uploadBuffer; +} + void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) { // TODO: Implement optimizations on the copy queue. On windows, use DirectStorage // TODO: Implement file source - auto stackMemoryArena = SystemGetStackMemoryArena(); SystemAssert(commandList != ELEM_HANDLE_NULL); @@ -654,14 +745,54 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem auto resourceData = GetDirectX12GraphicsResourceData(parameters->Resource); SystemAssert(resourceData); + ReadOnlySpan sourceData; + + if (parameters->SourceType == ElemCopyDataSourceType_Memory) + { + sourceData = ReadOnlySpan(parameters->SourceMemoryData.Items, parameters->SourceMemoryData.Length); + } + + auto uploadBuffer = GetUploadBuffer(commandList, sourceData.Length); + + SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); + memcpy(uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset, sourceData.Pointer, sourceData.Length); + if (resourceData->Type == ElemGraphicsResourceType_Buffer) { - //commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject, parameters->BufferOffset, ID3D12Resource *pSrcBuffer, parameters., UINT64 NumBytes) + commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject.Get(), parameters->BufferOffset, uploadBuffer.PoolItem->Buffer.Get(), uploadBuffer.Offset, sourceData.Length); } else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) { //commandListData->DeviceObject->CopyTextureRegion(const D3D12_TEXTURE_COPY_LOCATION *pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION *pSrc, const D3D12_BOX *pSrcBox) } + + /* + // TODO: File Source + + // TODO: This is just a sanity check for now. + // TODO: We need to lock this area and maybe create a bigger buffer, etc. + // To do so fast, we can have a double buffer approach so the pending operations are not impacted and everyone switch to the + // new bigger buffer + // TODO: The best way is to write functions in graphics device like resource descriptor heap + // TODO: Call a function GetDirectX12ScratchBuffer(size) and then ReleaseDirectX12ScratchBuffer(fence); + // add a list of scratch buffers in the current command list and when submitted, call the functions with the fence + if (graphicsDeviceData->CurrentUploadBufferOffset + sourceData.Length > graphicsDeviceData->UploadBuffer->GetDesc().Width) + { + SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "Upload Heap doesn't have enough memory. Resetting the offset."); + graphicsDeviceData->CurrentUploadBufferOffset = 0; + } + + auto dataOffset = SystemAtomicAdd(graphicsDeviceData->CurrentUploadBufferOffset, sourceData.Length); + memcpy(graphicsDeviceData->UploadBufferCpuPointer + dataOffset, sourceData.Pointer, sourceData.Length); + + if (resourceData->Type == ElemGraphicsResourceType_Buffer) + { + commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject.Get(), parameters->BufferOffset, graphicsDeviceData->UploadBuffer.Get(), dataOffset, sourceData.Length); + } + else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + //commandListData->DeviceObject->CopyTextureRegion(const D3D12_TEXTURE_COPY_LOCATION *pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION *pSrc, const D3D12_BOX *pSrcBox) + }*/ } ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) @@ -768,6 +899,11 @@ ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGra auto index = ConvertDirectX12DescriptorHandleToIndex(descriptorHeap, descriptorHandle); + if ((index % 1000) == 0) + { + SystemPlatformCommitMemory(&directX12ResourceDescriptorInfos[index], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + } + directX12ResourceDescriptorInfos[index].Resource = resource; directX12ResourceDescriptorInfos[index].Usage = usage; @@ -802,8 +938,13 @@ void DirectX12FreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor desc directX12ResourceDescriptorInfos[descriptor].Resource = ELEM_HANDLE_NULL; } -void DirectX12ProcessGraphicsResourceDeleteQueue() +void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { ProcessResourceDeleteQueue(); SystemClearMemoryArena(directX12ReadBackMemoryArena); + + auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + graphicsDeviceData->UploadBufferGeneration++; } diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index 6dceea29..6b64674b 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -66,6 +66,6 @@ ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGra ElemGraphicsResourceDescriptorInfo DirectX12GetGraphicsResourceDescriptorInfo(ElemGraphicsResourceDescriptor descriptor); void DirectX12FreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descriptor, const ElemFreeGraphicsResourceDescriptorOptions* options); -void DirectX12ProcessGraphicsResourceDeleteQueue(void); +void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); void DirectX12GraphicsResourceBarrier(ElemCommandList commandList, ElemGraphicsResourceDescriptor descriptor, const ElemGraphicsResourceBarrierOptions* options); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index 5a5e06ff..913a2094 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -478,5 +478,5 @@ void DirectX12PresentSwapChain(ElemSwapChain swapChain) #endif DirectX12ResetCommandAllocation(swapChainDataFull->GraphicsDevice); - DirectX12ProcessGraphicsResourceDeleteQueue(); + DirectX12ProcessGraphicsResourceDeleteQueue(swapChainDataFull->GraphicsDevice); } diff --git a/src/Elemental/Microsoft/UnityBuild.cpp b/src/Elemental/Microsoft/UnityBuild.cpp index 46ecbd32..2d0d6432 100644 --- a/src/Elemental/Microsoft/UnityBuild.cpp +++ b/src/Elemental/Microsoft/UnityBuild.cpp @@ -33,6 +33,7 @@ #include "Graphics/Resource.cpp" #include "Graphics/Shader.cpp" #include "Graphics/Rendering.cpp" +#include "Graphics/UploadBufferPool.cpp" #include "Inputs/Inputs.cpp" #include "Inputs/HidDevices.cpp" diff --git a/src/Elemental/Microsoft/Win32Application.cpp b/src/Elemental/Microsoft/Win32Application.cpp index ca780e78..fba2a1f7 100644 --- a/src/Elemental/Microsoft/Win32Application.cpp +++ b/src/Elemental/Microsoft/Win32Application.cpp @@ -92,8 +92,28 @@ ElemAPI ElemSystemInfo ElemGetSystemInfo() }; } +// TODO: Put that in system functions +ReadOnlySpan FormatMemorySize(MemoryArena memoryArena, uint64_t bytes) +{ + const char* suffixes[] = { "B", "KB", "MB", "GB", "TB" }; // Extend if more are needed + double size = bytes; + size_t i = 0; + + while (size >= 1024 && i < sizeof(suffixes)/sizeof(suffixes[0]) - 1) + { + size /= 1024.0; + i++; + } + + auto result = SystemPushArray(memoryArena, 50); + snprintf(result.Pointer, result.Length, "%.2f %s", size, suffixes[i]); + + return result; +} + ElemAPI int32_t ElemRunApplication(const ElemRunApplicationParameters* parameters) { + auto stackMemoryArena = SystemGetStackMemoryArena(); InitWin32ApplicationMemory(); if (parameters->InitHandler) @@ -120,6 +140,12 @@ ElemAPI int32_t ElemRunApplication(const ElemRunApplicationParameters* parameter if (parameters->FreeHandler) { + auto allocationInfos = SystemGetAllocationInfos(); + + SystemLogDebugMessage(ElemLogMessageCategory_Application, "Allocated lib memory before releasing: %s/%s", + FormatMemorySize(stackMemoryArena, allocationInfos.CommittedBytes).Pointer, + FormatMemorySize(stackMemoryArena, allocationInfos.ReservedBytes).Pointer); + parameters->FreeHandler(parameters->Payload); } diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 576ba39f..15f789cf 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -514,7 +514,7 @@ UTEST(Resource, FreeGraphicsResource_WithFenceNotExecuted) ElemFreeGraphicsResource(resource, &options); // Assert - ElemProcessGraphicsResourceDeleteQueue(); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); ElemFreeGraphicsResource(resource, nullptr); @@ -546,7 +546,7 @@ UTEST(Resource, FreeGraphicsResource_WithFenceExecuted) // Assert ElemWaitForFenceOnCpu(fence); - ElemProcessGraphicsResourceDeleteQueue(); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); auto afterFreeResourceInfo = ElemGetGraphicsResourceInfo(resource); ElemFreeGraphicsHeap(graphicsHeap); @@ -709,7 +709,7 @@ UTEST(Resource, DownloadGraphicsBufferData_WithNoReadbackHeap) auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); // Act - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + ElemDownloadGraphicsBufferData(resource, nullptr); // Assert ElemFreeGraphicsResource(resource, nullptr); @@ -833,6 +833,147 @@ UTEST(Resource, CopyDataToGraphicsResource_WithBuffer) ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); } +// TODO: Do the same test but with the copy operations spread accross multiple frames +// TODO: Bigger item count to check the error +UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopies) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Readback + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); + + auto itemCount = 20000u; + auto resourceSize = itemCount * sizeof(uint32_t); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto data = (uint32_t*)malloc(resourceSize); + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + // Act + for (uint32_t i = 0; i < itemCount; i++) + { + for (uint32_t j = 0; j < itemCount; j++) + { + data[j] = i; + } + + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + } + + // Assert + ElemCommitCommandList(commandList); + + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto bufferValues = (uint32_t*)resourceDataSpan.Items; + + for (uint32_t i = 0; i < itemCount; i++) + { + ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); + } + + free(data); +} + +UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopiesCommandLists) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Readback + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); + + auto itemCount = 50000u; + auto resourceSize = itemCount * sizeof(uint32_t); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto data = (uint32_t*)malloc(resourceSize); + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + + // Act + ElemFence fence; + + for (uint32_t i = 0; i < itemCount; i++) + { + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + for (uint32_t j = 0; j < itemCount; j++) + { + data[j] = i; + } + + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + ElemCommitCommandList(commandList); + fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + } + + // Assert + ElemWaitForFenceOnCpu(fence); + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto bufferValues = (uint32_t*)resourceDataSpan.Items; + + for (uint32_t i = 0; i < itemCount; i++) + { + ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); + } + + free(data); +} + +// TODO: File source +// TODO: Textures +// TODO: Big source (so that the upload heap can grow) +// TODO: Test buffer offset and length if too much + UTEST(Resource, CreateGraphicsResourceDescriptor_ReadWithBuffer) { // Arrange @@ -857,6 +998,35 @@ UTEST(Resource, CreateGraphicsResourceDescriptor_ReadWithBuffer) ASSERT_EQ_MSG(descriptorInfo.Usage, ElemGraphicsResourceDescriptorUsage_Read, "Usage should be equals to the one used during creation."); } +UTEST(Resource, CreateGraphicsResourceDescriptor_BigAmount) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + ElemGraphicsResourceDescriptor descriptor; + + for (uint32_t i = 0; i < 100000; i++) + { + descriptor = ElemCreateGraphicsResourceDescriptor(resource, ElemGraphicsResourceDescriptorUsage_Read, nullptr); + } + + // Assert + auto descriptorInfo = ElemGetGraphicsResourceDescriptorInfo(descriptor); + + ElemFreeGraphicsResourceDescriptor(descriptor, nullptr); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(descriptorInfo.Resource, resource, "Resource should be equals to the one used during creation."); + ASSERT_EQ_MSG(descriptorInfo.Usage, ElemGraphicsResourceDescriptorUsage_Read, "Usage should be equals to the one used during creation."); +} + UTEST(Resource, CreateGraphicsResourceDescriptor_WriteWithBufferWrite) { // Arrange @@ -1009,7 +1179,7 @@ UTEST(Resource, FreeGraphicsResourceDescriptor_WithFenceNotExecuted) ElemFreeGraphicsResourceDescriptor(descriptor, &options); // Assert - ElemProcessGraphicsResourceDeleteQueue(); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); auto descriptorInfo = ElemGetGraphicsResourceDescriptorInfo(descriptor); ElemFreeGraphicsResourceDescriptor(descriptor, nullptr); @@ -1043,7 +1213,7 @@ UTEST(Resource, FreeGraphicsResourceDescriptor_WithFenceExecuted) // Assert ElemWaitForFenceOnCpu(fence); - ElemProcessGraphicsResourceDeleteQueue(); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); auto descriptorInfo = ElemGetGraphicsResourceDescriptorInfo(descriptor); ElemFreeGraphicsResource(resource, nullptr); From 673c8a7f166c5e420ed75fe1baab591c8832f961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 4 Jan 2025 12:49:16 +0100 Subject: [PATCH 53/93] WIP Texture loading --- samples/Common/SampleGpuMemory.h | 18 ++- samples/Common/SampleScene.h | 1 + samples/Common/SampleSceneLoader.h | 104 ++++++++++++++---- samples/Elemental/06-HelloMesh/main.c | 8 +- .../99-Renderer/Data/RenderMesh.hlsl | 44 +++++++- samples/Elemental/99-Renderer/main.c | 76 ++++++++----- .../ElementalTools/02-SceneCompiler/main.c | 1 + .../Graphics/DirectX12CommandList.cpp | 1 + .../Graphics/DirectX12GraphicsDevice.cpp | 2 +- .../Microsoft/Graphics/DirectX12Resource.cpp | 40 ++++++- src/ElementalTools/ElementalTools.h | 2 +- .../SceneLoading/SceneLoaderObj.cpp | 5 +- tests/GraphicsTests/ResourceTests.cpp | 2 +- 13 files changed, 239 insertions(+), 65 deletions(-) diff --git a/samples/Common/SampleGpuMemory.h b/samples/Common/SampleGpuMemory.h index 235df1ff..ecaff9b5 100644 --- a/samples/Common/SampleGpuMemory.h +++ b/samples/Common/SampleGpuMemory.h @@ -42,8 +42,7 @@ void SampleFreeGpuMemory(SampleGpuMemory* gpuMemory) gpuMemory->GraphicsHeap = ELEM_HANDLE_NULL; } -// TODO: To remove when IOQueues -SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, const void* dataPointer, uint32_t sizeInBytes, const char* debugName) +SampleGpuBuffer SampleCreateGpuBuffer(SampleGpuMemory* gpuMemory, uint32_t sizeInBytes, const char* debugName) { // TODO: Alignment should be used with the offset before adding the size of the resource! ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(gpuMemory->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, &(ElemGraphicsResourceInfoOptions) { .DebugName = debugName }); @@ -54,8 +53,6 @@ SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, c ElemGraphicsResourceDescriptor readDescriptor = ElemCreateGraphicsResourceDescriptor(buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); - ElemUploadGraphicsBufferData(buffer, 0, (ElemDataSpan) { .Items = (uint8_t*)dataPointer, .Length = sizeInBytes }); - return (SampleGpuBuffer) { .Buffer = buffer, @@ -63,6 +60,19 @@ SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, c }; } +// TODO: To remove when IOQueues +SampleGpuBuffer SampleCreateGpuBufferAndUploadData(SampleGpuMemory* gpuMemory, const void* dataPointer, uint32_t sizeInBytes, const char* debugName) +{ + SampleGpuBuffer result = SampleCreateGpuBuffer(gpuMemory, sizeInBytes, debugName); + ElemUploadGraphicsBufferData(result.Buffer, 0, (ElemDataSpan) { .Items = (uint8_t*)dataPointer, .Length = sizeInBytes }); + + return (SampleGpuBuffer) + { + .Buffer = result.Buffer, + .ReadDescriptor = result.ReadDescriptor + }; +} + void SampleFreeGpuBuffer(SampleGpuBuffer* gpuBuffer) { ElemFreeGraphicsResourceDescriptor(gpuBuffer->ReadDescriptor, NULL); diff --git a/samples/Common/SampleScene.h b/samples/Common/SampleScene.h index b8ebc106..5b411e91 100644 --- a/samples/Common/SampleScene.h +++ b/samples/Common/SampleScene.h @@ -53,6 +53,7 @@ typedef struct uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; + int32_t MaterialId; } SampleMeshPrimitiveHeader; typedef struct diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 795cb1b0..cb6482f6 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -31,6 +31,14 @@ typedef struct SampleTextureData NormalTexture; } SampleMaterialData; +// TODO: Do otherwise +typedef struct +{ + int32_t AlbedoTextureId; + int32_t NormalTextureId; + ElemVector4 AlbedoFactor; +} ShaderMaterial; + typedef struct { uint32_t MeshCount; @@ -39,6 +47,7 @@ typedef struct SampleMaterialData* Materials; uint32_t NodeCount; SampleSceneNodeHeader* Nodes; + SampleGpuBuffer MaterialBuffer; } SampleSceneData; void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) @@ -65,22 +74,31 @@ void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) } // TODO: We should be able to replace this whole function with IO Queues -void SampleLoadMeshData(SampleMeshData* meshData, SampleGpuMemory* gpuMemory) +void SampleLoadMeshData(ElemCommandList commandList, SampleMeshData* meshData, SampleGpuMemory* gpuMemory) { + // TODO: Construct debug name + meshData->MeshBuffer = SampleCreateGpuBuffer(gpuMemory, meshData->MeshHeader.MeshBufferSizeInBytes, meshData->MeshHeader.Name); + + // TODO: To replace with file IO assert(meshData->Path); + uint8_t* meshBufferData = (uint8_t*)malloc(sizeof(uint8_t) * meshData->MeshHeader.MeshBufferSizeInBytes); FILE* file = SampleOpenFile(meshData->Path, true); assert(file); fseek(file, meshData->MeshHeader.MeshBufferOffset, SEEK_SET); - - uint8_t* meshBufferData = (uint8_t*)malloc(sizeof(uint8_t) * meshData->MeshHeader.MeshBufferSizeInBytes); fread(meshBufferData, sizeof(uint8_t), meshData->MeshHeader.MeshBufferSizeInBytes, file); + fclose(file); - // TODO: Construct debug name - meshData->MeshBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, meshBufferData, meshData->MeshHeader.MeshBufferSizeInBytes, meshData->MeshHeader.Name); + ElemCopyDataToGraphicsResourceParameters copyParameters = + { + .Resource = meshData->MeshBuffer.Buffer, + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)meshBufferData, .Length = meshData->MeshHeader.MeshBufferSizeInBytes } + }; - fclose(file); + ElemCopyDataToGraphicsResource(commandList, ©Parameters); + free(meshBufferData); } void SampleFreeMesh(SampleMeshData* meshData) @@ -93,7 +111,7 @@ void SampleFreeMesh(SampleMeshData* meshData) // TODO: Free mallocs } -void SampleLoadTexture(const char* path, SampleTextureData* textureData) +void SampleLoadTexture(const char* path, SampleTextureData* textureData, SampleGpuMemory* gpuMemory) { *textureData = (SampleTextureData){}; @@ -111,24 +129,44 @@ void SampleLoadTexture(const char* path, SampleTextureData* textureData) fread(textureData->MipDataEntries, sizeof(SampleTextureDataBlockEntry), textureData->TextureHeader.MipCount, file); fclose(file); -} - -// TODO: In the future we will need to load individual mips from disk -void SampleLoadTextureData(SampleTextureData* textureData, SampleGpuMemory* gpuMemory) -{ - assert(textureData->Path); + // TODO: For now we create the texture here but later, we should do it on the fly and update the material buffer // TODO: Get texture name without folder and extension // TODO: Get the correct format textureData->GpuTexture = SampleCreateGpuTexture(gpuMemory, textureData->TextureHeader.Width, textureData->TextureHeader.Height, textureData->TextureHeader.MipCount, ElemGraphicsFormat_BC7_SRGB, textureData->Path); +} - FILE* file = SampleOpenFile(textureData->Path, true); - assert(file); - - //fseek(file, meshData->MeshHeader.MeshBufferOffset, SEEK_SET); +// TODO: In the future we will need to load individual mips from disk +void SampleLoadTextureData(ElemCommandList commandList, SampleTextureData* textureData, SampleGpuMemory* gpuMemory) +{ + assert(textureData->Path); - fclose(file); + // TODO: Replace that with file logic + for (uint32_t i = 0; i < textureData->TextureHeader.MipCount; i++) + { + SampleTextureDataBlockEntry mipEntry = textureData->MipDataEntries[i]; + uint8_t* mipData = (uint8_t*)malloc(sizeof(uint8_t) * mipEntry.SizeInBytes); + + FILE* file = SampleOpenFile(textureData->Path, true); + assert(file); + + fseek(file, mipEntry.Offset, SEEK_SET); + fread(mipData, sizeof(uint8_t), mipEntry.SizeInBytes, file); + fclose(file); + + ElemCopyDataToGraphicsResourceParameters copyParameters = + { + .Resource = textureData->GpuTexture.Texture, + .TextureMipLevel = i, + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)mipData, .Length = mipEntry.SizeInBytes } + }; + + ElemCopyDataToGraphicsResource(commandList, ©Parameters); + free(mipData); + } + // TODO: This is not true, the data will be loaded when the command list is executed textureData->IsLoaded = true; } @@ -141,11 +179,15 @@ void SampleFreeTexture(SampleTextureData* textureData) // TODO: Free mallocs } -void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleMaterialData* materialData, const char* directoryPath) +void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleMaterialData* materialData, ShaderMaterial* shaderMaterial, SampleGpuMemory* gpuMemory, const char* directoryPath) { *materialData = (SampleMaterialData){}; materialData->MaterialHeader = *materialHeader; + shaderMaterial->AlbedoFactor = materialData->MaterialHeader.AlbedoFactor; + shaderMaterial->AlbedoTextureId = -1; + shaderMaterial->NormalTextureId = -1; + // TODO: Some materials can use the same texture so we need to load it only once if (strlen(materialHeader->AlbedoTexturePath) > 0) { @@ -153,7 +195,8 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM strcpy(fullTexturePath, directoryPath); strcat(fullTexturePath, materialHeader->AlbedoTexturePath); - SampleLoadTexture(fullTexturePath, &materialData->AlbedoTexture); + SampleLoadTexture(fullTexturePath, &materialData->AlbedoTexture, gpuMemory); + shaderMaterial->AlbedoTextureId = materialData->AlbedoTexture.GpuTexture.ReadDescriptor; } // TODO: NormalMap @@ -167,7 +210,7 @@ void SampleFreeMaterial(SampleMaterialData* materialData) } } -void SampleLoadScene(const char* path, SampleSceneData* sceneData) +void SampleLoadScene(const char* path, SampleSceneData* sceneData, SampleGpuMemory* gpuMemory) { // TODO: When IOQueues are implemented, we only need to read the header, not the whole file! @@ -211,16 +254,24 @@ void SampleLoadScene(const char* path, SampleSceneData* sceneData) char directoryPath[MAX_PATH]; GetFileDirectory(path, directoryPath, MAX_PATH); - for (uint32_t i = 0; i < sceneData->MaterialCount; i++) + if (sceneHeader.MaterialCount > 0) { - SampleLoadMaterial(&materialHeaders[i], &sceneData->Materials[i], directoryPath); + ShaderMaterial* shaderMaterials = (ShaderMaterial*)malloc(sizeof(ShaderMaterial) * sceneHeader.MaterialCount); + + for (uint32_t i = 0; i < sceneData->MaterialCount; i++) + { + SampleLoadMaterial(&materialHeaders[i], &sceneData->Materials[i], &shaderMaterials[i], gpuMemory, directoryPath); + } + + sceneData->MaterialBuffer = SampleCreateGpuBufferAndUploadData(gpuMemory, shaderMaterials, sceneHeader.MaterialCount * sizeof(ShaderMaterial), "MaterialBuffer"); + free(shaderMaterials); } free(materialHeaders); free(meshDataBlocks); } -void SampleFreeScene(const SampleSceneData* sceneData) +void SampleFreeScene(SampleSceneData* sceneData) { for (uint32_t i = 0; i < sceneData->MeshCount; i++) { @@ -231,4 +282,9 @@ void SampleFreeScene(const SampleSceneData* sceneData) { SampleFreeMaterial(&sceneData->Materials[i]); } + + if (sceneData->MaterialBuffer.Buffer != ELEM_HANDLE_NULL) + { + SampleFreeGpuBuffer(&sceneData->MaterialBuffer); + } } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index f517b711..e29fa18e 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -112,9 +112,13 @@ void InitSample(void* payload) applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(256)); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - SampleLoadScene("kitten.scene", &applicationPayload->TestSceneData); - SampleLoadMeshData(&applicationPayload->TestSceneData.Meshes[0], &applicationPayload->GpuMemory); + SampleLoadScene("kitten.scene", &applicationPayload->TestSceneData, &applicationPayload->GpuMemory); //SampleLoadScene("buddha.scene", &applicationPayload->TestSceneData); + + ElemCommandList loadDataCommandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + SampleLoadMeshData(loadDataCommandList, &applicationPayload->TestSceneData.Meshes[0], &applicationPayload->GpuMemory); + ElemCommitCommandList(loadDataCommandList); + ElemExecuteCommandList(applicationPayload->CommandQueue, loadDataCommandList, NULL); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 504a595a..1331d1b0 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -4,14 +4,14 @@ struct ShaderParameters { uint32_t FrameDataBufferIndex; uint32_t MeshBuffer; + uint32_t MaterialBuffer; uint32_t VertexBufferOffset; uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; - uint32_t Reserved1; float Scale; float3 Translation; - uint32_t Reserved2; + uint32_t MaterialId; float4 Rotation; }; @@ -24,6 +24,13 @@ struct FrameData uint32_t ShowMeshlets; }; +typedef struct +{ + int32_t AlbedoTextureId; + int32_t NormalTextureId; + float4 AlbedoFactor; +} ShaderMaterial; + struct ElemMeshlet { uint32_t VertexIndexOffset; @@ -43,7 +50,9 @@ struct VertexOutput { float4 Position: SV_Position; float3 WorldNormal: NORMAL0; + float2 TextureCoordinates: TEXCOORD0; uint MeshletIndex : COLOR0; + uint MaterialId : COLOR1; }; #define IDENTITY_MATRIX float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) @@ -84,7 +93,9 @@ void MeshMain(in uint groupId: SV_GroupID, // NOTE: This calculation is faster because v * M is faster than M * M vertices[groupThreadId].Position = mul(float4(worldPosition, 1.0), frameData.ViewProjMatrix); vertices[groupThreadId].WorldNormal = worldNormal; + vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; vertices[groupThreadId].MeshletIndex = groupId; + vertices[groupThreadId].MaterialId = parameters.MaterialId; } if (groupThreadId < meshlet.TriangleCount) @@ -114,13 +125,40 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 ByteAddressBuffer frameDataBuffer = ResourceDescriptorHeap[parameters.FrameDataBufferIndex]; FrameData frameData = frameDataBuffer.Load(0); + ByteAddressBuffer materialBuffer = ResourceDescriptorHeap[parameters.MaterialBuffer]; + ShaderMaterial material = materialBuffer.Load(parameters.MaterialId * sizeof(ShaderMaterial)); + if (frameData.ShowMeshlets == 0) { + if (material.AlbedoTextureId >= 0) + { + Texture2D albedoTexture = ResourceDescriptorHeap[material.AlbedoTextureId]; + + // TODO: This is a hack because we don't have a sampler for now + float width; + float height; + albedoTexture.GetDimensions(width, height); + + // Scale UV by texture size + float2 scaledUV = input.TextureCoordinates * float2(width, height); + + // Convert to integer. Use floor (or round) for nearest: + int coordX = ( (int)floor(scaledUV.x) % (int)width + (int)width ) % (int)width; + int coordY = ( (int)floor(scaledUV.y) % (int)height + (int)height ) % (int)height; + + // If your texture is oriented with y=0 at the top, you might flip here: + coordY = ((int)height - 1 - coordY); + + return albedoTexture.Load(int3(coordX, coordY, 0)); + } + + return float4(0, 0, 1, 1);; return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); } else { - uint hashResult = hash(input.MeshletIndex); + //uint hashResult = hash(input.MeshletIndex); + uint hashResult = hash(input.MaterialId); float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; return float4(meshletColor, 1.0); diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 04eb8aca..983c4a06 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -13,14 +13,14 @@ typedef struct uint32_t FrameDataBuffer; // TODO: Embed that into a buffer uint32_t MeshBuffer; + uint32_t MaterialBuffer; uint32_t VertexBufferOffset; uint32_t MeshletOffset; uint32_t MeshletVertexIndexOffset; uint32_t MeshletTriangleIndexOffset; - uint32_t Reserved1; float Scale; ElemVector3 Translation; - uint32_t Reserved2; + uint32_t MaterialId; ElemVector4 Rotation; } ShaderParameters; @@ -112,7 +112,7 @@ void InitSample(void* payload) applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBuffer.ReadDescriptor; CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); - SampleLoadScene(applicationPayload->ScenePath, &applicationPayload->TestSceneData); + SampleLoadScene(applicationPayload->ScenePath, &applicationPayload->TestSceneData, &applicationPayload->GpuMemory); ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -190,10 +190,9 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemInputStream inputStream = ElemGetInputStream(); SampleInputsApplicationUpdate(inputStream, &applicationPayload->InputsApplication, updateParameters->DeltaTimeInSeconds); - - // TODO: Use another type of camera SampleInputsCameraUpdate(inputStream, &applicationPayload->InputsCamera, updateParameters); + // TODO: We shold move this into the application update function (we can pass the window if needed) if (applicationPayload->InputsApplication.State.ExitApplication) { ElemExitApplication(0); @@ -214,6 +213,45 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void UpdateFrameData(applicationPayload, inputsCameraState->ViewProjMatrix, inputsCameraState->Action); + // TODO: We need to have a kind of queue system. The problem here is that if we don't have any + // data to load we will create empty lists + ElemCommandList loadDataCommandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + + // TODO: Fow now, we load all the material textures in one shot + // We do it in the main loop now because later it will be a streaming system. + // Doing it in the main loop force us to decouple the loading + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) + { + SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; + + if (material->AlbedoTexture.Path && !material->AlbedoTexture.IsLoaded) + { + SampleLoadTextureData(loadDataCommandList, &material->AlbedoTexture, &applicationPayload->GpuMemory); + } + } + + for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) + { + SampleSceneNodeHeader* sceneNode = &applicationPayload->TestSceneData.Nodes[i]; + + if (sceneNode->NodeType == SampleSceneNodeType_Mesh) + { + SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[sceneNode->ReferenceIndex]; + + if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) + { + // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data + // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check + // if the data was loaded. + // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. + SampleLoadMeshData(loadDataCommandList, meshData, &applicationPayload->GpuMemory); + } + } + } + + ElemCommitCommandList(loadDataCommandList); + ElemFence loadDataFence = ElemExecuteCommandList(applicationPayload->CommandQueue, loadDataCommandList, NULL); + ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) { @@ -234,18 +272,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemBindPipelineState(commandList, applicationPayload->GraphicsPipeline); - // TODO: Fow now, we load all the material textures in one shot - // We do it in the main loop now because later it will be a streaming system. - // Doing it in the main loop force us to decouple the loading - for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) - { - SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; - - if (material->AlbedoTexture.Path && !material->AlbedoTexture.IsLoaded) - { - SampleLoadTextureData(&material->AlbedoTexture, &applicationPayload->GpuMemory); - } - } + applicationPayload->ShaderParameters.MaterialBuffer = applicationPayload->TestSceneData.MaterialBuffer.ReadDescriptor; // TODO: Construct a list of tasks on the cpu for now and do only one dispatch mesh with the total of tasks // Be carreful with the limit per dimension of 65000 @@ -256,15 +283,8 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void if (sceneNode->NodeType == SampleSceneNodeType_Mesh) { SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[sceneNode->ReferenceIndex]; - - if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) - { - // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data - // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check - // if the data was loaded. - // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. - SampleLoadMeshData(meshData, &applicationPayload->GpuMemory); - } + + // TODO: We need to check if the data is loaded into the gpu applicationPayload->ShaderParameters.MeshBuffer = meshData->MeshBuffer.ReadDescriptor; @@ -277,6 +297,7 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void applicationPayload->ShaderParameters.MeshletTriangleIndexOffset = meshPrimitive->MeshletTriangleIndexOffset; applicationPayload->ShaderParameters.Scale = sceneNode->Scale; applicationPayload->ShaderParameters.Translation = sceneNode->Translation; + applicationPayload->ShaderParameters.MaterialId = meshPrimitive->MaterialId; applicationPayload->ShaderParameters.Rotation = sceneNode->Rotation; ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&applicationPayload->ShaderParameters, .Length = sizeof(ShaderParameters) }); @@ -288,7 +309,8 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemEndRenderPass(commandList); ElemCommitCommandList(commandList); - applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); + + applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, &(ElemExecuteCommandListOptions) { .FencesToWait = { .Items = &loadDataFence, .Length = 1 }}); ElemPresentSwapChain(applicationPayload->SwapChain); SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index b6b0e633..6cc7af8b 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -39,6 +39,7 @@ bool WriteMeshData(FILE* file, ElemSceneMesh mesh) } SampleMeshPrimitiveHeader* meshPrimitiveHeader = &meshPrimitiveHeaders[i]; + meshPrimitiveHeader->MaterialId = meshPrimitive->MaterialId; meshPrimitiveHeader->MeshletCount = result.Meshlets.Length; meshPrimitiveHeader->VertexBufferOffset = ftell(file) - meshHeader.MeshBufferOffset; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp index 89939524..7abcbffc 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp @@ -98,6 +98,7 @@ ElemCommandQueue DirectX12CreateCommandQueue(ElemGraphicsDevice graphicsDevice, commandQueue->SetName(SystemConvertUtf8ToWideChar(stackMemoryArena, options->DebugName).Pointer); } + // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) auto commandAllocators = SystemPushArray>(DirectX12MemoryArena, DIRECTX12_MAX_COMMANDLISTS); auto commandLists = SystemPushArray>(DirectX12MemoryArena, DIRECTX12_MAX_COMMANDLISTS * DIRECTX12_MAX_COMMANDLISTS); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index f8c26375..cb949843 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -410,6 +410,7 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions auto dsvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); auto rootSignature = CreateDirectX12RootSignature(device); + // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) auto uploadBuffers = SystemPushArray>*>(DirectX12MemoryArena, MAX_UPLOAD_BUFFERS); auto handle = SystemAddDataPoolItem(directX12GraphicsDevicePool, { @@ -453,7 +454,6 @@ void DirectX12FreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) if (uploadBuffer && uploadBuffer->Buffer) { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Deleting upload buffer"); uploadBuffer->Buffer.Reset(); *uploadBuffer = {}; } diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index d2d54c4b..5227c810 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -742,6 +742,9 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem auto commandListData = GetDirectX12CommandListData(commandList); SystemAssert(commandListData); + auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(commandListData->GraphicsDevice); + SystemAssert(graphicsDeviceData); + auto resourceData = GetDirectX12GraphicsResourceData(parameters->Resource); SystemAssert(resourceData); @@ -763,7 +766,42 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem } else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) { - //commandListData->DeviceObject->CopyTextureRegion(const D3D12_TEXTURE_COPY_LOCATION *pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION *pSrc, const D3D12_BOX *pSrcBox) + // TODO: Unit test first !!!!! + + // TODO: We should move this code on texture creation to cache the info for each mip level + auto textureDesc = resourceData->DeviceObject->GetDesc(); + UINT64 totalBytes = 0; + + D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedFootprint = {}; + + graphicsDeviceData->Device->GetCopyableFootprints( + &textureDesc, + parameters->TextureMipLevel, // First subresource + 1, // Number of subresources + uploadBuffer.Offset, // Base offset in the upload buffer + &placedFootprint, // [out] footprints + nullptr, // row size in bytes + nullptr, // row count + &totalBytes); // total bytes + + // TODO: We should call the GetFootprints with offset 0 at texture creation and then change it here + //placedFootprint.Offset = uploadBuffer.Offset; + + D3D12_TEXTURE_COPY_LOCATION sourceLocation = + { + .pResource = uploadBuffer.PoolItem->Buffer.Get(), + .Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, + .PlacedFootprint = placedFootprint + }; + + D3D12_TEXTURE_COPY_LOCATION destinationLocation = + { + .pResource = resourceData->DeviceObject.Get(), + .Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + .SubresourceIndex = parameters->TextureMipLevel + }; + + commandListData->DeviceObject->CopyTextureRegion(&destinationLocation, 0, 0, 0, &sourceLocation, nullptr); } /* diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 3a9a1206..1a4503c8 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -279,7 +279,7 @@ typedef struct ElemUInt32Span IndexBuffer; ElemToolsBoundingBox BoundingBox; // TODO: SphereVolume? - // TODO: Material + int32_t MaterialId; } ElemSceneMeshPrimitive; typedef struct diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index 2a040237..a4a4fc61 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -14,6 +14,7 @@ struct ObjMeshPrimitiveInfo { uint32_t IndexOffset; uint32_t IndexCount; + int32_t MaterialId; ElemToolsBoundingBox BoundingBox; }; @@ -222,11 +223,12 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E *meshPrimitiveInfo = { .IndexOffset = indexOffset, + .MaterialId = (int32_t)currentMaterial, .BoundingBox = { .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } - } + } }; for (uint32_t j = currentFaceStart; j < currentFaceEnd; j++) @@ -280,6 +282,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); meshPrimitive->IndexBuffer = ConstructObjIndexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); meshPrimitive->BoundingBox = meshPrimitiveInfo->BoundingBox; + meshPrimitive->MaterialId = meshPrimitiveInfo->MaterialId; AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); } diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 15f789cf..4ceded82 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -901,7 +901,7 @@ UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopies) free(data); } -UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopiesCommandLists) +UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandLists) { // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); From bdcb25f28d343e461a298bcabc839d8484a968af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 5 Jan 2025 08:07:41 +0100 Subject: [PATCH 54/93] WIP DirectX12 Texture loading --- .../ElementalTools/03-TextureCompiler/main.c | 2 +- .../Common/Graphics/UploadBufferPool.cpp | 31 ++++-- .../Common/Graphics/UploadBufferPool.h | 2 +- src/Elemental/Elemental.h | 1 + .../Graphics/DirectX12CommandList.cpp | 4 +- .../Microsoft/Graphics/DirectX12Config.h | 24 +++++ .../Graphics/DirectX12GraphicsDevice.cpp | 7 +- .../Graphics/DirectX12GraphicsDevice.h | 5 - .../Microsoft/Graphics/DirectX12Resource.cpp | 98 ++++++++----------- .../Microsoft/Graphics/DirectX12Resource.h | 10 ++ .../Microsoft/Graphics/DirectX12Shader.cpp | 3 - .../Microsoft/Graphics/DirectX12SwapChain.cpp | 1 - .../Microsoft/Graphics/DirectX12SwapChain.h | 3 +- tests/GraphicsTests/ResourceTests.cpp | 1 + 14 files changed, 104 insertions(+), 88 deletions(-) create mode 100644 src/Elemental/Microsoft/Graphics/DirectX12Config.h diff --git a/samples/ElementalTools/03-TextureCompiler/main.c b/samples/ElementalTools/03-TextureCompiler/main.c index 04592dda..797c01b7 100644 --- a/samples/ElementalTools/03-TextureCompiler/main.c +++ b/samples/ElementalTools/03-TextureCompiler/main.c @@ -90,7 +90,7 @@ int main(int argc, const char* argv[]) for (uint32_t i = 0; i < mipData.Length; i++) { ElemTextureMipData* mipLevelData = &mipData.Items[i]; - printf("Size: %dx%d\n", mipLevelData->Width, mipLevelData->Height); + printf("Mip level %d Size: %dx%d\n", i, mipLevelData->Width, mipLevelData->Height); ElemCompressTextureMipDataResult compressedTextureData = ElemCompressTextureMipData(ElemToolsGraphicsFormat_BC7, mipLevelData, NULL); diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp index 3e41f0a2..b659b5d2 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.cpp +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -2,7 +2,7 @@ #include "SystemFunctions.h" template -UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t sizeInBytes) +UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t alignment, uint64_t sizeInBytes) { if (uploadBufferPool->Generation != generation) { @@ -30,31 +30,42 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB currentUploadBuffer->IsResetNeeded = true; currentUploadBuffer->CurrentOffset = 0u; - uint64_t alignedSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512llu * 1024u * 1024u); + uint64_t uploadBufferSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512llu * 1024u * 1024u); - if (alignedSize == 0) + if (uploadBufferSize == 0) { // TODO: put that in a constant - alignedSize = 16u * 1024u * 1024u; + uploadBufferSize = 16u * 1024u * 1024u; } - while (alignedSize < sizeInBytes) + while (uploadBufferSize < sizeInBytes) { - alignedSize <<= 1; + uploadBufferSize <<= 1; } - uploadBufferPool->LastBufferSize = alignedSize; - currentUploadBuffer->SizeInBytes = alignedSize; + uploadBufferPool->LastBufferSize = uploadBufferSize; + currentUploadBuffer->SizeInBytes = uploadBufferSize; } // TODO: Check alignment auto offset = uploadBufferPool->CurrentUploadBuffer->CurrentOffset; - uploadBufferPool->CurrentUploadBuffer->CurrentOffset += sizeInBytes; + uint64_t alignedOffset = (offset + (alignment - 1)) & ~(alignment - 1); + + uint64_t newOffset = alignedOffset + sizeInBytes; + + if (newOffset > uploadBufferPool->CurrentUploadBuffer->SizeInBytes) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Not enough space after alignment"); + return { nullptr, 0ull }; + } + + // Update buffer offset to reflect our allocation + uploadBufferPool->CurrentUploadBuffer->CurrentOffset = newOffset; return { .PoolItem = uploadBufferPool->CurrentUploadBuffer, - .Offset = offset + .Offset = alignedOffset }; } diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.h b/src/Elemental/Common/Graphics/UploadBufferPool.h index a52fbaeb..ad438c08 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.h +++ b/src/Elemental/Common/Graphics/UploadBufferPool.h @@ -34,7 +34,7 @@ struct UploadBufferDevicePool }; template -UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t sizeInBytes); +UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t alignment, uint64_t sizeInBytes); /* template diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 089abdae..35d10e27 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -759,6 +759,7 @@ typedef struct uint32_t SourceFileOffset; uint32_t SourceFileSizeInBytes; ElemDataSpan SourceMemoryData; + // TODO: Allow specifying texture rowSizeInBytes? } ElemCopyDataToGraphicsResourceParameters; typedef struct diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp index 7abcbffc..f40fc119 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp @@ -1,12 +1,10 @@ #include "DirectX12CommandList.h" +#include "DirectX12Config.h" #include "DirectX12GraphicsDevice.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" -#define DIRECTX12_MAX_COMMANDQUEUES 10u -#define DIRECTX12_MAX_COMMANDLISTS 64u - SystemDataPool directX12CommandQueuePool; SystemDataPool directX12CommandListPool; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Config.h b/src/Elemental/Microsoft/Graphics/DirectX12Config.h new file mode 100644 index 00000000..51d8e1a9 --- /dev/null +++ b/src/Elemental/Microsoft/Graphics/DirectX12Config.h @@ -0,0 +1,24 @@ +#pragma once + +#define D3D12SDK_VERSION 614 +#define D3D12SDK_PATH ".\\" + +#define DIRECTX12_MEMORY_ARENA 128 * 1024 * 1024 +#define DIRECTX12_READBACK_MEMORY_ARENA 32 * 1024 * 1024 + +#define DIRECTX12_MAX_DEVICES 10u +#define DIRECTX12_MAX_RTVS 1024u + +#define DIRECTX12_MAX_COMMANDQUEUES 10u +#define DIRECTX12_MAX_COMMANDLISTS 64u + +#define DIRECTX12_MAX_GRAPHICSHEAP 32 +#define DIRECTX12_MAX_RESOURCES 1000000 +#define DIRECTX12_MAX_MIPS 16 + +#define DIRECTX12_MAX_SWAPCHAIN_BUFFERS 3 +#define DIRECTX12_MAX_SWAPCHAINS 10u + +#define DIRECTX12_MAX_LIBRARIES UINT16_MAX +#define DIRECTX12_MAX_PIPELINESTATES UINT16_MAX + diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index cb949843..231cd9f7 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -1,13 +1,10 @@ #include "DirectX12GraphicsDevice.h" +#include "DirectX12Config.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemLogging.h" #include "SystemMemory.h" -#define D3D12SDK_VERSION 614 -#define D3D12SDK_PATH ".\\" -#define DIRECTX12_MAX_RTVS 1024u - struct DirectX12DescriptorHeapFreeListItem { uint32_t Next; @@ -108,7 +105,7 @@ void InitDirectX12GraphicsDeviceMemory() if (!DirectX12MemoryArena.Storage) { // TODO: To review - DirectX12MemoryArena = SystemAllocateMemoryArena(128 * 1024 * 1024); + DirectX12MemoryArena = SystemAllocateMemoryArena(DIRECTX12_MEMORY_ARENA); directX12GraphicsDevicePool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_DEVICES); InitDirectX12(); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h index 7a9a04f8..586f177d 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h @@ -4,11 +4,6 @@ #include "Graphics/UploadBufferPool.h" #include "SystemMemory.h" -#define DIRECTX12_MAX_DEVICES 10u - -// TODO: Put this value a a config? -#define DIRECTX12_MAX_RESOURCES 1000000 - struct DirectX12DescriptorHeapStorage; struct DirectX12DescriptorHeap diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 5227c810..f4247eb5 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -1,4 +1,5 @@ #include "DirectX12Resource.h" +#include "DirectX12Config.h" #include "DirectX12GraphicsDevice.h" #include "DirectX12CommandList.h" #include "Graphics/Resource.h" @@ -8,8 +9,6 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -#define DIRECTX12_MAX_GRAPHICSHEAP 32 - SystemDataPool directX12GraphicsHeapPool; SystemDataPool directX12GraphicsResourcePool; @@ -31,7 +30,7 @@ void InitDirectX12ResourceMemory() directX12ResourceDescriptorInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES, AllocationState_Reserved); // TODO: Allow to increase the size as a parameter - directX12ReadBackMemoryArena = SystemAllocateMemoryArena(32 * 1024 * 1024); + directX12ReadBackMemoryArena = SystemAllocateMemoryArena(DIRECTX12_READBACK_MEMORY_ARENA); } } @@ -136,7 +135,7 @@ ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDev }); SystemAddDataPoolItemFull(directX12GraphicsResourcePool, handle, { - .GraphicsDevice = graphicsDevice + .GraphicsDevice = graphicsDevice, }); return handle; @@ -679,15 +678,12 @@ ComPtr CreateDirectX12UploadBuffer(ComPtr graphi return resource; } -UploadBufferMemory> GetUploadBuffer(ElemCommandList commandList, uint64_t sizeInBytes) +UploadBufferMemory> GetUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t alignment, uint64_t sizeInBytes) { - auto commandListData = GetDirectX12CommandListData(commandList); - SystemAssert(commandListData); - - auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(commandListData->GraphicsDevice); + auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); SystemAssert(graphicsDeviceData); - auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(commandListData->GraphicsDevice); + auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(graphicsDevice); auto uploadBufferPool = &threadDirectX12UploadBufferPools[graphicsIdUnpacked.Index]; if (!uploadBufferPool->IsInited) @@ -699,7 +695,7 @@ UploadBufferMemory> GetUploadBuffer(ElemCommandList comma uploadBufferPool->IsInited = true; } - auto uploadBuffer = GetUploadBufferPoolItem(uploadBufferPool, graphicsDeviceData->UploadBufferGeneration, sizeInBytes); + auto uploadBuffer = GetUploadBufferPoolItem(uploadBufferPool, graphicsDeviceData->UploadBufferGeneration, alignment, sizeInBytes); if (uploadBuffer.PoolItem->IsResetNeeded) { @@ -748,44 +744,60 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem auto resourceData = GetDirectX12GraphicsResourceData(parameters->Resource); SystemAssert(resourceData); + auto resourceDataFull = GetDirectX12GraphicsResourceDataFull(parameters->Resource); + SystemAssert(resourceDataFull); + ReadOnlySpan sourceData; + // TODO: File Source if (parameters->SourceType == ElemCopyDataSourceType_Memory) { sourceData = ReadOnlySpan(parameters->SourceMemoryData.Items, parameters->SourceMemoryData.Length); } - auto uploadBuffer = GetUploadBuffer(commandList, sourceData.Length); + // TODO: Unit test first !!!!! + + auto uploadBufferAlignment = 4u; + auto uploadBufferSizeInBytes = sourceData.Length; + DirectX12GraphicsTextureMipCopyInfo textureMipCopyInfo = {}; + + if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + uploadBufferAlignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT; + auto resourceDesc = resourceData->DeviceObject->GetDesc(); + + graphicsDeviceData->Device->GetCopyableFootprints(&resourceDesc, + parameters->TextureMipLevel, 1, 0, + &textureMipCopyInfo.PlacedFootprint, + &textureMipCopyInfo.RowCount, + &textureMipCopyInfo.SourceRowSizeInBytes, + &uploadBufferSizeInBytes); + } + auto uploadBuffer = GetUploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); - memcpy(uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset, sourceData.Pointer, sourceData.Length); if (resourceData->Type == ElemGraphicsResourceType_Buffer) { + memcpy(uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset, sourceData.Pointer, sourceData.Length); commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject.Get(), parameters->BufferOffset, uploadBuffer.PoolItem->Buffer.Get(), uploadBuffer.Offset, sourceData.Length); } else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) { - // TODO: Unit test first !!!!! + auto placedFootprint = textureMipCopyInfo.PlacedFootprint; + auto sourceRowSizeInBytes = textureMipCopyInfo.SourceRowSizeInBytes; - // TODO: We should move this code on texture creation to cache the info for each mip level - auto textureDesc = resourceData->DeviceObject->GetDesc(); - UINT64 totalBytes = 0; + auto destData = uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset; - D3D12_PLACED_SUBRESOURCE_FOOTPRINT placedFootprint = {}; + for (uint32_t i = 0; i < textureMipCopyInfo.RowCount; i++) + { + auto uploadBufferRowData = destData + i * placedFootprint.Footprint.RowPitch; + auto sourceRowData = sourceData.Pointer + i * sourceRowSizeInBytes; - graphicsDeviceData->Device->GetCopyableFootprints( - &textureDesc, - parameters->TextureMipLevel, // First subresource - 1, // Number of subresources - uploadBuffer.Offset, // Base offset in the upload buffer - &placedFootprint, // [out] footprints - nullptr, // row size in bytes - nullptr, // row count - &totalBytes); // total bytes + memcpy(uploadBufferRowData, sourceRowData, sourceRowSizeInBytes); + } - // TODO: We should call the GetFootprints with offset 0 at texture creation and then change it here - //placedFootprint.Offset = uploadBuffer.Offset; + placedFootprint.Offset = uploadBuffer.Offset; D3D12_TEXTURE_COPY_LOCATION sourceLocation = { @@ -803,34 +815,6 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem commandListData->DeviceObject->CopyTextureRegion(&destinationLocation, 0, 0, 0, &sourceLocation, nullptr); } - - /* - // TODO: File Source - - // TODO: This is just a sanity check for now. - // TODO: We need to lock this area and maybe create a bigger buffer, etc. - // To do so fast, we can have a double buffer approach so the pending operations are not impacted and everyone switch to the - // new bigger buffer - // TODO: The best way is to write functions in graphics device like resource descriptor heap - // TODO: Call a function GetDirectX12ScratchBuffer(size) and then ReleaseDirectX12ScratchBuffer(fence); - // add a list of scratch buffers in the current command list and when submitted, call the functions with the fence - if (graphicsDeviceData->CurrentUploadBufferOffset + sourceData.Length > graphicsDeviceData->UploadBuffer->GetDesc().Width) - { - SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "Upload Heap doesn't have enough memory. Resetting the offset."); - graphicsDeviceData->CurrentUploadBufferOffset = 0; - } - - auto dataOffset = SystemAtomicAdd(graphicsDeviceData->CurrentUploadBufferOffset, sourceData.Length); - memcpy(graphicsDeviceData->UploadBufferCpuPointer + dataOffset, sourceData.Pointer, sourceData.Length); - - if (resourceData->Type == ElemGraphicsResourceType_Buffer) - { - commandListData->DeviceObject->CopyBufferRegion(resourceData->DeviceObject.Get(), parameters->BufferOffset, graphicsDeviceData->UploadBuffer.Get(), dataOffset, sourceData.Length); - } - else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) - { - //commandListData->DeviceObject->CopyTextureRegion(const D3D12_TEXTURE_COPY_LOCATION *pDst, UINT DstX, UINT DstY, UINT DstZ, const D3D12_TEXTURE_COPY_LOCATION *pSrc, const D3D12_BOX *pSrcBox) - }*/ } ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index 6b64674b..99a3698c 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -17,9 +17,19 @@ struct DirectX12GraphicsHeapDataFull uint32_t reserved; }; +struct DirectX12GraphicsTextureMipCopyInfo +{ + uint32_t RowCount; + uint64_t SourceRowSizeInBytes; + uint32_t UploadBufferSizeInBytes; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT PlacedFootprint; +}; + +// TODO: Optimize this structure because it is the most used struct DirectX12GraphicsResourceData { ComPtr DeviceObject; + // TODO: We can maybe merge the two? D3D12_CPU_DESCRIPTOR_HANDLE RtvHandle; D3D12_CPU_DESCRIPTOR_HANDLE DsvHandle; ElemGraphicsResourceType Type; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 384e40a9..46301231 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -9,9 +9,6 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -#define DIRECTX12_MAX_LIBRARIES UINT16_MAX -#define DIRECTX12_MAX_PIPELINESTATES UINT16_MAX - SystemDataPool directX12ShaderLibraryPool; SystemDataPool directX12PipelineStatePool; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp index 913a2094..1cf6efb8 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.cpp @@ -9,7 +9,6 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -#define DIRECTX12_MAX_SWAPCHAINS 10u // TODO: Without FPS limit we can maybe debug UAV Barrier #define ENABLE_TEARING 0 diff --git a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h index 5c494287..9ae4e6f5 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12SwapChain.h @@ -1,9 +1,8 @@ #pragma once +#include "DirectX12Config.h" #include "Elemental.h" -#define DIRECTX12_MAX_SWAPCHAIN_BUFFERS 3 - // TODO: Review structure struct DirectX12SwapChainData { diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 4ceded82..400be540 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -971,6 +971,7 @@ UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandLists) // TODO: File source // TODO: Textures +// TODO: Check alignement after mip 5 // TODO: Big source (so that the upload heap can grow) // TODO: Test buffer offset and length if too much From 61114d1e29c9aa65ec0496ed24928708effb9f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 6 Jan 2025 14:44:32 +0100 Subject: [PATCH 55/93] Fix Apple Metal issues. --- samples/Common/SampleSceneLoader.h | 6 +- samples/Common/SampleUtils.h | 2 +- samples/Elemental/99-Renderer/main.c | 2 +- .../Apple/Graphics/MetalCommandList.h | 3 +- .../Apple/Graphics/MetalGraphicsDevice.cpp | 34 +- .../Apple/Graphics/MetalGraphicsDevice.h | 5 + .../Apple/Graphics/MetalResource.cpp | 237 ++++++++-- src/Elemental/Apple/Graphics/MetalResource.h | 4 +- .../Apple/Graphics/MetalSwapChain.cpp | 4 +- src/Elemental/Apple/MacOSApplication.cpp | 7 + src/Elemental/Apple/UnityBuild.cpp | 1 + .../Common/Graphics/UploadBufferPool.cpp | 2 +- .../Common/Graphics/Vulkan/VulkanResource.cpp | 4 +- src/Elemental/Common/SystemFunctions.cpp | 19 + src/Elemental/Common/SystemFunctions.h | 2 + src/Elemental/Elemental.h | 1 + .../Microsoft/Graphics/DirectX12Resource.cpp | 6 +- src/Elemental/Microsoft/Win32Application.cpp | 31 +- tests/GraphicsTests/ResourceIOTests.cpp | 421 ++++++++++++++++++ tests/GraphicsTests/ResourceTests.cpp | 418 ----------------- tests/GraphicsTests/UnityBuild.cpp | 1 + utilities/cmake/AppPackaging.cmake | 55 ++- 22 files changed, 770 insertions(+), 495 deletions(-) create mode 100644 tests/GraphicsTests/ResourceIOTests.cpp diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index cb6482f6..685f7aa2 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -113,10 +113,13 @@ void SampleFreeMesh(SampleMeshData* meshData) void SampleLoadTexture(const char* path, SampleTextureData* textureData, SampleGpuMemory* gpuMemory) { + // TODO: Here we need to use a global list of texture that are unique based on the path + // For now the same texture is loaded multiple time which is really bad + *textureData = (SampleTextureData){}; // TODO: Do better here - char* tmp = malloc(strlen(path)); + char* tmp = malloc(strlen(path) + 1); strcpy(tmp, path); textureData->Path = tmp; @@ -192,6 +195,7 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM if (strlen(materialHeader->AlbedoTexturePath) > 0) { char fullTexturePath[MAX_PATH]; + memset(fullTexturePath, 0, MAX_PATH); strcpy(fullTexturePath, directoryPath); strcat(fullTexturePath, materialHeader->AlbedoTexturePath); diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index cecbdb31..cf1e64b2 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -85,7 +85,7 @@ FILE* SampleOpenFile(const char* filename, bool prefixData) char absolutePath[MAX_PATH]; SampleGetFullPath(absolutePath, filename, prefixData); - //printf("Read path: %s\n", absolutePath); + //printf("Read path: '%s'\n", absolutePath); return fopen(absolutePath, "rb"); } diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 983c4a06..fcf40367 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -329,7 +329,7 @@ int main(int argc, const char* argv[]) ApplicationPayload payload = { .AppSettings = SampleParseAppSettings(argc, argv), - .ScenePath = "sponza/sponza.scene" + .ScenePath = "Sponza/sponza.scene" }; int32_t scenePathIndex = argc - 1; diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.h b/src/Elemental/Apple/Graphics/MetalCommandList.h index 7c783b29..f12b6327 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.h +++ b/src/Elemental/Apple/Graphics/MetalCommandList.h @@ -7,7 +7,8 @@ enum MetalCommandEncoderType { MetalCommandEncoderType_None = 0, MetalCommandEncoderType_Render = 1, - MetalCommandEncoderType_Compute = 2 + MetalCommandEncoderType_Compute = 2, + MetalCommandEncoderType_Copy = 3 }; enum MetalResourceBarrierType diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp index 8cc1b091..58495cee 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp @@ -4,7 +4,6 @@ #include "SystemLogging.h" #include "SystemMemory.h" -#define METAL_MAXDEVICES 10u struct MetalArgumentBufferDescriptor { @@ -44,6 +43,7 @@ void* MetalDebugReportCallback(void* arg) while ((bytesRead = read(fd, buffer, sizeof(buffer))) > 0) { buffer[bytesRead - 1] = '\0'; + //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, buffer); auto splittedStrings = SystemSplitString(stackMemoryArena, ReadOnlySpan(buffer), ']'); if (splittedStrings.Length == 2) @@ -72,6 +72,7 @@ void InitMetal() auto stackMemoryArena = SystemGetStackMemoryArena(); // TODO: There is nothing in code to enable that 😢 + return; if (MetalDebugLayerEnabled) { @@ -115,7 +116,7 @@ void InitMetalGraphicsDeviceMemory() { // TODO: To Review MetalGraphicsMemoryArena = SystemAllocateMemoryArena(128 * 1024 * 1024); - metalGraphicsDevicePool = SystemCreateDataPool(MetalGraphicsMemoryArena, METAL_MAXDEVICES); + metalGraphicsDevicePool = SystemCreateDataPool(MetalGraphicsMemoryArena, METAL_MAX_DEVICES); InitMetal(); } @@ -285,7 +286,7 @@ ElemGraphicsDeviceInfoSpan MetalGetAvailableGraphicsDevices() InitMetalGraphicsDeviceMemory(); auto stackMemoryArena = SystemGetStackMemoryArena(); - auto deviceInfos = SystemPushArray(stackMemoryArena, METAL_MAXDEVICES); + auto deviceInfos = SystemPushArray(stackMemoryArena, METAL_MAX_DEVICES); auto currentDeviceInfoIndex = 0u; auto device = NS::TransferPtr(MTL::CreateSystemDefaultDevice()); @@ -328,11 +329,15 @@ ElemGraphicsDevice MetalCreateGraphicsDevice(const ElemGraphicsDeviceOptions* op residencySet->addAllocation(resourceArgumentBuffer.Storage->ArgumentBuffer.get()); residencySet->commit(); + + // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) + auto uploadBuffers = SystemPushArray>*>(MetalGraphicsMemoryArena, MAX_UPLOAD_BUFFERS); auto handle = SystemAddDataPoolItem(metalGraphicsDevicePool, { .Device = device, .ResourceArgumentBuffer = resourceArgumentBuffer, - .MemoryArena = memoryArena + .MemoryArena = memoryArena, + .UploadBufferPools = uploadBuffers }); SystemAddDataPoolItemFull(metalGraphicsDevicePool, handle, { @@ -349,6 +354,27 @@ void MetalFreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) auto graphicsDeviceData = GetMetalGraphicsDeviceData(graphicsDevice); SystemAssert(graphicsDeviceData); + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + for (uint32_t j = 0; j < MAX_UPLOAD_BUFFERS; j++) + { + auto uploadBuffer = &bufferPool->UploadBuffers[j]; + + if (uploadBuffer && uploadBuffer->Buffer) + { + uploadBuffer->Buffer.reset(); + *uploadBuffer = {}; + } + } + + *bufferPool = {}; + } + } + FreeMetalArgumentBuffer(graphicsDeviceData->ResourceArgumentBuffer); SystemFreeMemoryArena(graphicsDeviceData->MemoryArena); diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h index 3cb38543..ad23d722 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h @@ -1,8 +1,10 @@ #pragma once +#include "Graphics/UploadBufferPool.h" #include "Elemental.h" #include "SystemMemory.h" +#define METAL_MAX_DEVICES 10u // TODO: Increase to 1.000.000? #define METAL_MAX_RESOURCES 500000 @@ -18,6 +20,9 @@ struct MetalGraphicsDeviceData NS::SharedPtr Device; MetalArgumentBuffer ResourceArgumentBuffer; MemoryArena MemoryArena; + uint64_t UploadBufferGeneration; + Span>*> UploadBufferPools; + uint32_t CurrentUploadBufferPoolIndex; }; struct MetalGraphicsDeviceDataFull diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index 8a8960ef..c8650d4f 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -1,17 +1,23 @@ #include "MetalResource.h" #include "MetalGraphicsDevice.h" #include "MetalCommandList.h" +#include "Graphics/UploadBufferPool.h" #include "Graphics/ResourceDeleteQueue.h" #include "Graphics/Resource.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" +// TODO: Move that to config header like DirectX12 #define METAL_MAX_GRAPHICSHEAP 32 +#define METAL_READBACK_MEMORY_ARENA 32 * 1024 * 1024 SystemDataPool metalGraphicsHeapPool; SystemDataPool metalResourcePool; Span metalResourceDescriptorInfos; +MemoryArena metalReadBackMemoryArena; + +thread_local UploadBufferDevicePool> threadDirectX12UploadBufferPools[METAL_MAX_DEVICES]; void InitMetalResourceMemory() { @@ -22,6 +28,9 @@ void InitMetalResourceMemory() // TODO: This should be part of the graphics device data metalResourceDescriptorInfos = SystemPushArray(MetalGraphicsMemoryArena, METAL_MAX_RESOURCES, AllocationState_Reserved); + + // TODO: Allow to increase the size as a parameter + metalReadBackMemoryArena = SystemAllocateMemoryArena(METAL_READBACK_MEMORY_ARENA); } } @@ -65,7 +74,7 @@ MTL::PixelFormat ConvertToMetalResourceFormat(ElemGraphicsFormat format) return MTL::PixelFormatDepth32Float; case ElemGraphicsFormat_BC7_SRGB: - return MTL:PixelFormatBC7_RGBAUnorm_sRGB; + return MTL::PixelFormatBC7_RGBAUnorm_sRGB; default: return MTL::PixelFormatR8Unorm; @@ -91,7 +100,7 @@ ElemGraphicsFormat ConvertFromMetalResourceFormat(MTL::PixelFormat format) case MTL::PixelFormatDepth32Float: return ElemGraphicsFormat_D32_FLOAT; - case MTL:PixelFormatBC7_RGBAUnorm_sRGB: + case MTL::PixelFormatBC7_RGBAUnorm_sRGB: return ElemGraphicsFormat_BC7_SRGB; default: @@ -117,7 +126,7 @@ MTL::TextureUsage ConvertToMetalResourceUsage(ElemGraphicsResourceUsage usage) return result; } -ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture) +ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap graphicsHeap, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture) { InitMetalResourceMemory(); @@ -142,6 +151,7 @@ ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice auto handle = SystemAddDataPoolItem(metalResourcePool, { .DeviceObject = resource, + .GraphicsHeap = graphicsHeap, .Type = type, .Width = width, .Height = height, @@ -183,6 +193,13 @@ NS::SharedPtr CreateMetalTextureDescriptor(const ElemGra ElemGraphicsHeap MetalCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes, const ElemGraphicsHeapOptions* options) { InitMetalResourceMemory(); + + auto heapType = ElemGraphicsHeapType_Gpu; + + if (options) + { + heapType = options->HeapType; + } SystemAssert(graphicsDevice != ELEM_HANDLE_NULL); @@ -214,6 +231,7 @@ ElemGraphicsHeap MetalCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint .DeviceObject = graphicsHeap, .SizeInBytes = sizeInBytes, .GraphicsDevice = graphicsDevice, + .HeapType = heapType }); SystemAddDataPoolItemFull(metalGraphicsHeapPool, handle, { @@ -377,7 +395,7 @@ ElemGraphicsResource MetalCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, resource->setLabel(NS::String::string(resourceInfo->DebugName, NS::UTF8StringEncoding)); } - return CreateMetalGraphicsResourceFromResource(graphicsHeapData->GraphicsDevice, resourceInfo->Type, resourceInfo->Usage, resource, false); + return CreateMetalGraphicsResourceFromResource(graphicsHeapData->GraphicsDevice, resourceInfo->Type, graphicsHeap, resourceInfo->Usage, resource, false); } void MetalFreeGraphicsResource(ElemGraphicsResource resource, const ElemFreeGraphicsResourceOptions* options) @@ -420,36 +438,65 @@ ElemGraphicsResourceInfo MetalGetGraphicsResourceInfo(ElemGraphicsResource resou .Usage = resourceData->Usage }; } -/* -ElemDataSpan MetalGetGraphicsResourceDataSpan(ElemGraphicsResource resource) + +void MetalUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) { SystemAssert(resource != ELEM_HANDLE_NULL); auto resourceData = GetMetalResourceData(resource); SystemAssert(resourceData); + auto heapData = GetMetalGraphicsHeapData(resourceData->GraphicsHeap); + SystemAssert(heapData); + if (resourceData->Type != ElemGraphicsResourceType_Buffer) { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "GetGraphicsResourceDataSpan only works with graphics buffers."); - return {}; + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers."); + return; } - auto dataPointer = ((MTL::Buffer*)resourceData->DeviceObject.get())->contents(); - - return + if (heapData->HeapType != ElemGraphicsHeapType_GpuUpload) { - .Items = (uint8_t*)dataPointer, - .Length = resourceData->Width - }; -}*/ - -void MetalUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offset, ElemDataSpan data) -{ + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); + return; + } + + auto dataPointer = ((MTL::Buffer*)resourceData->DeviceObject.get())->contents(); + + auto destinationPointer = (uint8_t*)dataPointer + offset; + memcpy(destinationPointer, data.Items, data.Length); } ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) { - /*auto offset = 0u; + SystemAssert(resource != ELEM_HANDLE_NULL); + + auto resourceData = GetMetalResourceData(resource); + SystemAssert(resourceData); + + auto heapData = GetMetalGraphicsHeapData(resourceData->GraphicsHeap); + SystemAssert(heapData); + + if (resourceData->Type != ElemGraphicsResourceType_Buffer) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers."); + return {}; + } + + if (heapData->HeapType != ElemGraphicsHeapType_Readback && heapData->HeapType != ElemGraphicsHeapType_GpuUpload) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap. (%d)", heapData->HeapType); + return {}; + } + + if (heapData->HeapType != ElemGraphicsHeapType_Readback) + { + SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData works faster with graphics buffers allocated in a Readback heap."); + } + + auto dataPointer = ((MTL::Buffer*)resourceData->DeviceObject.get())->contents(); + + auto offset = 0u; auto sizeInBytes = resourceData->Width; if (options) @@ -462,16 +509,145 @@ ElemDataSpan MetalDownloadGraphicsBufferData(ElemGraphicsResource resource, cons } } - auto stackMemoryArena = SystemGetStackMemoryArena(); - auto downloadedData = SystemPushArray(directX12ReadBackMemoryArena, sizeInBytes); - memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); + auto downloadedData = SystemPushArray(metalReadBackMemoryArena, sizeInBytes); + memcpy(downloadedData.Pointer, (uint8_t*)dataPointer + offset, sizeInBytes); - return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length };*/ - return {}; + return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; +} + +NS::SharedPtr CreateMetalUploadBuffer(MTL::Device* graphicsDevice, uint64_t sizeInBytes) +{ + auto resource = NS::TransferPtr(graphicsDevice->newBuffer(sizeInBytes, MTL::ResourceHazardTrackingModeUntracked)); + + if (MetalDebugLayerEnabled) + { + resource->setLabel(NS::String::string("ElementalUploadBuffer", NS::UTF8StringEncoding)); + } + + return resource; +} + +UploadBufferMemory> GetMetalUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t alignment, uint64_t sizeInBytes) +{ + auto graphicsDeviceData = GetMetalGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(graphicsDevice); + auto uploadBufferPool = &threadDirectX12UploadBufferPools[graphicsIdUnpacked.Index]; + + // TODO: Review this + if (!uploadBufferPool->IsInited) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init pool"); + + auto poolIndex = SystemAtomicAdd(graphicsDeviceData->CurrentUploadBufferPoolIndex, 1); + graphicsDeviceData->UploadBufferPools[poolIndex] = uploadBufferPool; + uploadBufferPool->IsInited = true; + } + + auto uploadBuffer = GetUploadBufferPoolItem(uploadBufferPool, graphicsDeviceData->UploadBufferGeneration, alignment, sizeInBytes); + + if (uploadBuffer.PoolItem->IsResetNeeded) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to create upload buffer with size: %d...", uploadBuffer.PoolItem->SizeInBytes); + + if (uploadBuffer.PoolItem->Fence.FenceValue > 0) + { + MetalWaitForFenceOnCpu(uploadBuffer.PoolItem->Fence); + } + + // TODO: We need another flag to know if we need to delete + if (uploadBuffer.PoolItem->Buffer.get() != nullptr) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to delete buffer"); + uploadBuffer.PoolItem->Buffer.reset(); + } + + uploadBuffer.PoolItem->Buffer = CreateMetalUploadBuffer(graphicsDeviceData->Device.get(), uploadBuffer.PoolItem->SizeInBytes); + + uploadBuffer.PoolItem->CpuPointer = (uint8_t*)uploadBuffer.PoolItem->Buffer->contents(); + uploadBuffer.PoolItem->IsResetNeeded = false; + } + + return uploadBuffer; } void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) { + // TODO: Implement file source + auto stackMemoryArena = SystemGetStackMemoryArena(); + + SystemAssert(commandList != ELEM_HANDLE_NULL); + + SystemAssert(parameters); + SystemAssert(parameters->Resource != ELEM_HANDLE_NULL); + + auto commandListData = GetMetalCommandListData(commandList); + SystemAssert(commandListData); + + auto graphicsDeviceData = GetMetalGraphicsDeviceData(commandListData->GraphicsDevice); + SystemAssert(graphicsDeviceData); + + auto resourceData = GetMetalResourceData(parameters->Resource); + SystemAssert(resourceData); + + ReadOnlySpan sourceData; + + // TODO: File Source + if (parameters->SourceType == ElemCopyDataSourceType_Memory) + { + sourceData = ReadOnlySpan(parameters->SourceMemoryData.Items, parameters->SourceMemoryData.Length); + } + + // TODO: Unit test first !!!!! + + auto uploadBufferAlignment = 4u; + auto uploadBufferSizeInBytes = sourceData.Length; + + if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + if (resourceData->Format == ElemGraphicsFormat_BC7_SRGB) + { + uploadBufferAlignment = 16u; + uploadBufferSizeInBytes = SystemAlignToPowerOf2(uploadBufferSizeInBytes, 16u); + } + } + + auto uploadBuffer = GetMetalUploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); + SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); + + if (commandListData->CommandEncoderType != MetalCommandEncoderType_Copy) + { + ResetMetalCommandEncoder(commandList); + + commandListData->CommandEncoder = NS::RetainPtr(commandListData->DeviceObject->blitCommandEncoder()); + commandListData->CommandEncoderType = MetalCommandEncoderType_Copy; + } + + auto copyCommandEncoder = (MTL::BlitCommandEncoder*)commandListData->CommandEncoder.get(); + memcpy(uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset, sourceData.Pointer, sourceData.Length); + + if (resourceData->Type == ElemGraphicsResourceType_Buffer) + { + copyCommandEncoder->copyFromBuffer(uploadBuffer.PoolItem->Buffer.get(), uploadBuffer.Offset, (MTL::Buffer*)resourceData->DeviceObject.get(), parameters->BufferOffset, sourceData.Length); + } + else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + auto mipLevel = parameters->TextureMipLevel; + + auto mipWidth = SystemMax(1u, resourceData->Width >> mipLevel); + auto mipHeight = SystemMax(1u, resourceData->Height >> mipLevel); + + auto sourceBytesPerRow = mipWidth * 4; + + if (resourceData->Format == ElemGraphicsFormat_BC7_SRGB) + { + sourceBytesPerRow = ((mipWidth + 3) / 4) * 16; + } + + copyCommandEncoder->copyFromBuffer(uploadBuffer.PoolItem->Buffer.get(), uploadBuffer.Offset, sourceBytesPerRow, 0, MTL::Size(mipWidth, mipHeight, 1), + (MTL::Texture*)resourceData->DeviceObject.get(), 0, parameters->TextureMipLevel, MTL::Origin(0, 0, 0)); + } } ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) @@ -511,13 +687,16 @@ ElemGraphicsResourceDescriptor MetalCreateGraphicsResourceDescriptor(ElemGraphic handle = CreateMetalArgumentBufferHandleForBuffer(graphicsDeviceData->ResourceArgumentBuffer, (MTL::Buffer*)resourceData->DeviceObject.get(), resourceData->Width); } - if ((handle % 1000) == 0) + if (handle != -1) { - SystemPlatformCommitMemory(&metalResourceDescriptorInfos[descriptorHandle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); - } + if ((handle % 1000) == 0) + { + SystemCommitMemory(MetalGraphicsMemoryArena, &metalResourceDescriptorInfos[handle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + } - metalResourceDescriptorInfos[handle].Resource = resource; - metalResourceDescriptorInfos[handle].Usage = usage; + metalResourceDescriptorInfos[handle].Resource = resource; + metalResourceDescriptorInfos[handle].Usage = usage; + } return handle; } diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index b12558ee..d44868b5 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -7,6 +7,7 @@ struct MetalGraphicsHeapData NS::SharedPtr DeviceObject; uint64_t SizeInBytes; ElemGraphicsDevice GraphicsDevice; + ElemGraphicsHeapType HeapType; }; struct MetalGraphicsHeapDataFull @@ -17,6 +18,7 @@ struct MetalGraphicsHeapDataFull struct MetalResourceData { NS::SharedPtr DeviceObject; + ElemGraphicsHeap GraphicsHeap; ElemGraphicsResourceType Type; uint32_t Width; uint32_t Height; @@ -59,4 +61,4 @@ void MetalFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descript void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); -ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture); +ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap graphicsHeap, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture); diff --git a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp index 1a274bf3..b5122738 100644 --- a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp +++ b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp @@ -292,7 +292,7 @@ void MetalDisplayLinkHandler::metalDisplayLinkNeedsUpdate(CA::MetalDisplayLink* } // TODO: Can we do something better than juste creating/destroying each time? - auto backBufferTexture = CreateMetalGraphicsResourceFromResource(swapChainData->GraphicsDevice, ElemGraphicsResourceType_Texture2D, ElemGraphicsResourceUsage_RenderTarget, NS::RetainPtr(swapChainData->BackBufferDrawable->texture()), true); + auto backBufferTexture = CreateMetalGraphicsResourceFromResource(swapChainData->GraphicsDevice, ElemGraphicsResourceType_Texture2D, ELEM_HANDLE_NULL, ElemGraphicsResourceUsage_RenderTarget, NS::RetainPtr(swapChainData->BackBufferDrawable->texture()), true); ElemSwapChainUpdateParameters updateParameters = { @@ -314,6 +314,6 @@ void MetalDisplayLinkHandler::metalDisplayLinkNeedsUpdate(CA::MetalDisplayLink* MetalFreeGraphicsResource(backBufferTexture, nullptr); - MetalProcessGraphicsResourceDeleteQueue(); + MetalProcessGraphicsResourceDeleteQueue(swapChainData->GraphicsDevice); } } diff --git a/src/Elemental/Apple/MacOSApplication.cpp b/src/Elemental/Apple/MacOSApplication.cpp index 3fa155d3..3c66e434 100644 --- a/src/Elemental/Apple/MacOSApplication.cpp +++ b/src/Elemental/Apple/MacOSApplication.cpp @@ -195,6 +195,13 @@ bool MacOSApplicationDelegate::applicationShouldTerminateAfterLastWindowClosed(N NS::TerminateReply MacOSApplicationDelegate::applicationShouldTerminate(NS::Application* pSender) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto allocationInfos = SystemGetAllocationInfos(); + + SystemLogDebugMessage(ElemLogMessageCategory_Application, "Allocated lib memory before releasing: %s/%s", + SystemFormatMemorySize(stackMemoryArena, allocationInfos.CommittedBytes).Pointer, + SystemFormatMemorySize(stackMemoryArena, allocationInfos.ReservedBytes).Pointer); + ApplicationExited = true; if (_runParameters && _runParameters->FreeHandler) diff --git a/src/Elemental/Apple/UnityBuild.cpp b/src/Elemental/Apple/UnityBuild.cpp index aa61ba4b..f7f7fd5d 100644 --- a/src/Elemental/Apple/UnityBuild.cpp +++ b/src/Elemental/Apple/UnityBuild.cpp @@ -21,6 +21,7 @@ #include "Graphics/ShaderReader.cpp" #include "Graphics/ResourceDeleteQueue.cpp" #include "Graphics/ResourceBarrier.cpp" +#include "Graphics/UploadBufferPool.cpp" #include "Graphics/GraphicsDevice.cpp" #include "Graphics/CommandList.cpp" #include "Graphics/SwapChain.cpp" diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp index b659b5d2..b835bbae 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.cpp +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -49,7 +49,7 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB // TODO: Check alignment auto offset = uploadBufferPool->CurrentUploadBuffer->CurrentOffset; - uint64_t alignedOffset = (offset + (alignment - 1)) & ~(alignment - 1); + uint64_t alignedOffset = (offset + (alignment - 1)) & ~(alignment - 1); // TODO: Use System Align power of 2 uint64_t newOffset = alignedOffset + sizeInBytes; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index abe22d30..87ebf1d0 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -640,8 +640,8 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi if ((descriptorHandle % 1000) == 0) { - SystemPlatformCommitMemory(&vulkanResourceDescriptorInfos[descriptorHandle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); - SystemPlatformCommitMemory(&vulkanResourceDescriptorImageViews[descriptorHandle], 1000 * sizeof(VkImageView)); + SystemCommitMemory(VulkanGraphicsMemoryArena, &vulkanResourceDescriptorInfos[descriptorHandle], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + SystemCommitMemory(VulkanGraphicsMemoryArena, &vulkanResourceDescriptorImageViews[descriptorHandle], 1000 * sizeof(VkImageView)); } VkWriteDescriptorSet descriptor = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; diff --git a/src/Elemental/Common/SystemFunctions.cpp b/src/Elemental/Common/SystemFunctions.cpp index 3494572f..a4e961b2 100644 --- a/src/Elemental/Common/SystemFunctions.cpp +++ b/src/Elemental/Common/SystemFunctions.cpp @@ -432,6 +432,25 @@ ReadOnlySpan SystemConvertWideCharToUtf8(MemoryArena memoryArena, ReadOnly return destination; } +ReadOnlySpan SystemFormatMemorySize(MemoryArena memoryArena, uint64_t bytes) +{ + const char* suffixes[] = { "B", "KB", "MB", "GB", "TB" }; // Extend if more are needed + double size = bytes; + size_t i = 0; + + while (size >= 1024 && i < sizeof(suffixes)/sizeof(suffixes[0]) - 1) + { + size /= 1024.0; + i++; + } + + auto result = SystemPushArray(memoryArena, 50); + // TODO: Get rid of this + snprintf(result.Pointer, result.Length, "%.2f %s", size, suffixes[i]); + + return result; +} + //--------------------------------------------------------------------------------------------------------------- // IO functions diff --git a/src/Elemental/Common/SystemFunctions.h b/src/Elemental/Common/SystemFunctions.h index 3a40afd8..60016b45 100644 --- a/src/Elemental/Common/SystemFunctions.h +++ b/src/Elemental/Common/SystemFunctions.h @@ -199,6 +199,8 @@ ReadOnlySpan SystemConvertUtf8ToWideChar(MemoryArena memoryArena, ReadO */ ReadOnlySpan SystemConvertWideCharToUtf8(MemoryArena memoryArena, ReadOnlySpan source); +// TODO: Unit test +ReadOnlySpan SystemFormatMemorySize(MemoryArena memoryArena, uint64_t bytes); //--------------------------------------------------------------------------------------------------------------- // IO functions diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 35d10e27..13161b68 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -908,6 +908,7 @@ typedef struct typedef union { + // TODO: Fix compilation warning struct { float X, Y, Z, W; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index f4247eb5..985e4440 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -678,7 +678,7 @@ ComPtr CreateDirectX12UploadBuffer(ComPtr graphi return resource; } -UploadBufferMemory> GetUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t alignment, uint64_t sizeInBytes) +UploadBufferMemory> GetDirectX12UploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t alignment, uint64_t sizeInBytes) { auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); SystemAssert(graphicsDeviceData); @@ -774,7 +774,7 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem &uploadBufferSizeInBytes); } - auto uploadBuffer = GetUploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); + auto uploadBuffer = GetDirectX12UploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); if (resourceData->Type == ElemGraphicsResourceType_Buffer) @@ -923,7 +923,7 @@ ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGra if ((index % 1000) == 0) { - SystemPlatformCommitMemory(&directX12ResourceDescriptorInfos[index], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + SystemCommitMemory(DirectX12MemoryArena, &directX12ResourceDescriptorInfos[index], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); } directX12ResourceDescriptorInfos[index].Resource = resource; diff --git a/src/Elemental/Microsoft/Win32Application.cpp b/src/Elemental/Microsoft/Win32Application.cpp index fba2a1f7..e8a3e76e 100644 --- a/src/Elemental/Microsoft/Win32Application.cpp +++ b/src/Elemental/Microsoft/Win32Application.cpp @@ -92,25 +92,6 @@ ElemAPI ElemSystemInfo ElemGetSystemInfo() }; } -// TODO: Put that in system functions -ReadOnlySpan FormatMemorySize(MemoryArena memoryArena, uint64_t bytes) -{ - const char* suffixes[] = { "B", "KB", "MB", "GB", "TB" }; // Extend if more are needed - double size = bytes; - size_t i = 0; - - while (size >= 1024 && i < sizeof(suffixes)/sizeof(suffixes[0]) - 1) - { - size /= 1024.0; - i++; - } - - auto result = SystemPushArray(memoryArena, 50); - snprintf(result.Pointer, result.Length, "%.2f %s", size, suffixes[i]); - - return result; -} - ElemAPI int32_t ElemRunApplication(const ElemRunApplicationParameters* parameters) { auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -138,14 +119,14 @@ ElemAPI int32_t ElemRunApplication(const ElemRunApplicationParameters* parameter } } - if (parameters->FreeHandler) - { - auto allocationInfos = SystemGetAllocationInfos(); + auto allocationInfos = SystemGetAllocationInfos(); - SystemLogDebugMessage(ElemLogMessageCategory_Application, "Allocated lib memory before releasing: %s/%s", - FormatMemorySize(stackMemoryArena, allocationInfos.CommittedBytes).Pointer, - FormatMemorySize(stackMemoryArena, allocationInfos.ReservedBytes).Pointer); + SystemLogDebugMessage(ElemLogMessageCategory_Application, "Allocated lib memory before releasing: %s/%s", + SystemFormatMemorySize(stackMemoryArena, allocationInfos.CommittedBytes).Pointer, + SystemFormatMemorySize(stackMemoryArena, allocationInfos.ReservedBytes).Pointer); + if (parameters->FreeHandler) + { parameters->FreeHandler(parameters->Payload); } diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp new file mode 100644 index 00000000..19e2c103 --- /dev/null +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -0,0 +1,421 @@ +#include "Elemental.h" +#include "GraphicsTests.h" +#include "utest.h" + +// TODO: File source +// TODO: Textures: Check each mip map validity with different colors +// TODO: Check alignement after mip 5 +// TODO: Big source (so that the upload heap can grow) +// TODO: Test buffer offset and length if too much + +UTEST(ResourceIO, UploadGraphicsBufferData_WithBuffer) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); +} + +UTEST(ResourceIO, UploadGraphicsBufferData_WithNoGpuUploadHeap) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Gpu + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); +} + +UTEST(ResourceIO, UploadGraphicsBufferData_WithBufferOffsetSize) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t data[] = { 1, 2, 3, 4 }; + ElemUploadGraphicsBufferData(resource, 2, { .Items = data, .Length = 2 }); + + // Act + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); + ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); +} + +UTEST(ResourceIO, UploadGraphicsBufferData_WithTexture2D) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_SRGB, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + uint8_t data[] = { 1, 2, 3, 4 }; + + // Act + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers."); +} + +UTEST(ResourceIO, DownloadGraphicsBufferData_WithBuffer) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + ASSERT_EQ_MSG(resourceDataSpan.Length, 1024u, "Resource dataspan length should be equals to buffer size."); +} + +UTEST(ResourceIO, DownloadGraphicsBufferData_WithNoReadbackHeap) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Gpu + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("DownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap."); +} + +UTEST(ResourceIO, DownloadGraphicsBufferData_WithBufferOffsetAndSize) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t data[] = { 1, 2, 3, 4 }; + ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); + + // Act + ElemDownloadGraphicsBufferDataOptions downloadOptions = + { + .Offset = 2, + .SizeInBytes = 2 + }; + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, &downloadOptions); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + ASSERT_EQ_MSG(resourceDataSpan.Length, 2u, "Resource dataspan length should be equals to specified SizeInBytes."); + ASSERT_EQ_MSG(resourceDataSpan.Items[0], data[2], "Resource dataspan element 0 should be equal to test data at offset 2."); +} + +UTEST(ResourceIO, DownloadGraphicsBufferData_WithTexture2D) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); + auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_SRGB, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + // Act + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + // Assert + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_MESSAGE("ElemDownloadGraphicsBufferData only works with graphics buffers."); + + ASSERT_TRUE_MSG(resourceDataSpan.Items == nullptr, "Resource dataspan pointer should be null."); + ASSERT_EQ_MSG(resourceDataSpan.Length, 0u, "Resource dataspan length should be 0."); +} + +UTEST(ResourceIO, CopyDataToGraphicsResource_WithBuffer) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t data[] = { 1, 2, 3, 4 }; + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + // Act + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = 2, + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = data, .Length = 2 } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + + // Assert + ElemCommitCommandList(commandList); + + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); + + ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); + ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); + ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); +} + +// TODO: Do the same test but with the copy operations spread accross multiple frames +// TODO: Bigger item count to check the error +UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopies) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Readback + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); + + auto itemCount = 20000u; + auto resourceSize = itemCount * sizeof(uint32_t); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto data = (uint32_t*)malloc(resourceSize); + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + // Act + for (uint32_t i = 0; i < itemCount; i++) + { + for (uint32_t j = 0; j < itemCount; j++) + { + data[j] = i; + } + + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + } + + // Assert + ElemCommitCommandList(commandList); + + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto bufferValues = (uint32_t*)resourceDataSpan.Items; + + for (uint32_t i = 0; i < itemCount; i++) + { + ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); + } + + free(data); +} + +UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandLists) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_Readback + }; + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); + + auto itemCount = 50000u; + auto resourceSize = itemCount * sizeof(uint32_t); + auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto data = (uint32_t*)malloc(resourceSize); + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + + // Act + ElemFence fence; + + for (uint32_t i = 0; i < itemCount; i++) + { + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + for (uint32_t j = 0; j < itemCount; j++) + { + data[j] = i; + } + + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + ElemCommitCommandList(commandList); + fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + } + + // Assert + ElemWaitForFenceOnCpu(fence); + + auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + + auto bufferValues = (uint32_t*)resourceDataSpan.Items; + + for (uint32_t i = 0; i < itemCount; i++) + { + ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); + } + + free(data); +} diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 400be540..291f6328 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -557,424 +557,6 @@ UTEST(Resource, FreeGraphicsResource_WithFenceExecuted) ASSERT_EQ_MSG(afterFreeResourceInfo.Width, 0u, "Width should be equals to 0."); } -UTEST(Resource, UploadGraphicsBufferData_WithBuffer) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - uint8_t data[] = { 1, 2, 3, 4 }; - - - // Act - ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); -} - -UTEST(Resource, UploadGraphicsBufferData_WithNoGpuUploadHeap) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_Gpu - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - uint8_t data[] = { 1, 2, 3, 4 }; - - - // Act - ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); -} - -UTEST(Resource, UploadGraphicsBufferData_WithBufferOffsetSize) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - uint8_t data[] = { 1, 2, 3, 4 }; - ElemUploadGraphicsBufferData(resource, 2, { .Items = data, .Length = 2 }); - - // Act - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); - ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); - ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); -} - -UTEST(Resource, UploadGraphicsBufferData_WithTexture2D) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_SRGB, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - uint8_t data[] = { 1, 2, 3, 4 }; - - // Act - ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_MESSAGE("UploadGraphicsBufferData only works with graphics buffers."); -} - -UTEST(Resource, DownloadGraphicsBufferData_WithBuffer) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - // Act - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); - ASSERT_EQ_MSG(resourceDataSpan.Length, 1024u, "Resource dataspan length should be equals to buffer size."); -} - -UTEST(Resource, DownloadGraphicsBufferData_WithNoReadbackHeap) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_Gpu - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - // Act - ElemDownloadGraphicsBufferData(resource, nullptr); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_MESSAGE("DownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap."); -} - -UTEST(Resource, DownloadGraphicsBufferData_WithBufferOffsetAndSize) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - uint8_t data[] = { 1, 2, 3, 4 }; - ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); - - // Act - ElemDownloadGraphicsBufferDataOptions downloadOptions = - { - .Offset = 2, - .SizeInBytes = 2 - }; - - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, &downloadOptions); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); - ASSERT_EQ_MSG(resourceDataSpan.Length, 2u, "Resource dataspan length should be equals to specified SizeInBytes."); - ASSERT_EQ_MSG(resourceDataSpan.Items[0], data[2], "Resource dataspan element 0 should be equal to test data at offset 2."); -} - -UTEST(Resource, DownloadGraphicsBufferData_WithTexture2D) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), nullptr); - auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, 256, 256, 1, ElemGraphicsFormat_B8G8R8A8_SRGB, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - // Act - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - // Assert - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_MESSAGE("ElemDownloadGraphicsBufferData only works with graphics buffers."); - - ASSERT_TRUE_MSG(resourceDataSpan.Items == nullptr, "Resource dataspan pointer should be null."); - ASSERT_EQ_MSG(resourceDataSpan.Length, 0u, "Resource dataspan length should be 0."); -} - -UTEST(Resource, CopyDataToGraphicsResource_WithBuffer) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(1), &options); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, 1024u, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - uint8_t data[] = { 1, 2, 3, 4 }; - - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - // Act - ElemCopyDataToGraphicsResourceParameters parameters = - { - .Resource = resource, - .BufferOffset = 2, - .SourceType = ElemCopyDataSourceType_Memory, - .SourceMemoryData = { .Items = data, .Length = 2 } - }; - - ElemCopyDataToGraphicsResource(commandList, ¶meters); - - // Assert - ElemCommitCommandList(commandList); - - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - ElemWaitForFenceOnCpu(fence); - - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - ASSERT_TRUE_MSG(resourceDataSpan.Items != nullptr, "Resource dataspan pointer should not be null."); - - ASSERT_EQ_MSG(resourceDataSpan.Items[0], 0, "Resource dataspan element 0 should be equal to 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[1], 0, "Resource dataspan element 1 should be equal to 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[2], data[0], "Resource dataspan element 2 should be equal to test data at offset 0."); - ASSERT_EQ_MSG(resourceDataSpan.Items[3], data[1], "Resource dataspan element 3 should be equal to test data at offset 1."); - ASSERT_EQ_MSG(resourceDataSpan.Items[4], 0, "Resource dataspan element 2 should be equal to 0."); -} - -// TODO: Do the same test but with the copy operations spread accross multiple frames -// TODO: Bigger item count to check the error -UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopies) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_Readback - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); - - auto itemCount = 20000u; - auto resourceSize = itemCount * sizeof(uint32_t); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - auto data = (uint32_t*)malloc(resourceSize); - - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - // Act - for (uint32_t i = 0; i < itemCount; i++) - { - for (uint32_t j = 0; j < itemCount; j++) - { - data[j] = i; - } - - ElemCopyDataToGraphicsResourceParameters parameters = - { - .Resource = resource, - .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), - .SourceType = ElemCopyDataSourceType_Memory, - .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } - }; - - ElemCopyDataToGraphicsResource(commandList, ¶meters); - } - - // Assert - ElemCommitCommandList(commandList); - - auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - ElemWaitForFenceOnCpu(fence); - - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - auto bufferValues = (uint32_t*)resourceDataSpan.Items; - - for (uint32_t i = 0; i < itemCount; i++) - { - ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); - } - - free(data); -} - -UTEST(Resource, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandLists) -{ - // Arrange - auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_Readback - }; - - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(256), &options); - - auto itemCount = 50000u; - auto resourceSize = itemCount * sizeof(uint32_t); - auto resourceInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, resourceSize, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); - - auto data = (uint32_t*)malloc(resourceSize); - - auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); - - // Act - ElemFence fence; - - for (uint32_t i = 0; i < itemCount; i++) - { - auto commandList = ElemGetCommandList(commandQueue, nullptr); - - for (uint32_t j = 0; j < itemCount; j++) - { - data[j] = i; - } - - ElemCopyDataToGraphicsResourceParameters parameters = - { - .Resource = resource, - .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), - .SourceType = ElemCopyDataSourceType_Memory, - .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } - }; - - ElemCopyDataToGraphicsResource(commandList, ¶meters); - ElemCommitCommandList(commandList); - fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); - } - - // Assert - ElemWaitForFenceOnCpu(fence); - - auto resourceDataSpan = ElemDownloadGraphicsBufferData(resource, nullptr); - - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); - - ASSERT_LOG_NOERROR(); - - auto bufferValues = (uint32_t*)resourceDataSpan.Items; - - for (uint32_t i = 0; i < itemCount; i++) - { - ASSERT_EQ_MSG(bufferValues[i], i, "Resource dataspan element should be equal to initial data."); - } - - free(data); -} - -// TODO: File source -// TODO: Textures -// TODO: Check alignement after mip 5 -// TODO: Big source (so that the upload heap can grow) -// TODO: Test buffer offset and length if too much - UTEST(Resource, CreateGraphicsResourceDescriptor_ReadWithBuffer) { // Arrange diff --git a/tests/GraphicsTests/UnityBuild.cpp b/tests/GraphicsTests/UnityBuild.cpp index 8fbf0fc8..db1e05ee 100644 --- a/tests/GraphicsTests/UnityBuild.cpp +++ b/tests/GraphicsTests/UnityBuild.cpp @@ -4,6 +4,7 @@ #include "ShaderTests.cpp" #include "SwapChainTests.cpp" #include "ResourceTests.cpp" +#include "ResourceIOTests.cpp" #include "ResourceBarrierTests.cpp" #include "RenderingTests.cpp" #include "utest.h" diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index 4ca65814..c89c5cda 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -219,12 +219,6 @@ function(configure_project_package target_name install_folder) MACOSX_BUNDLE "TRUE" ) - if(NOT resources_length EQUAL 0) - set_target_properties(${target_name} PROPERTIES - RESOURCE "${ARG_RESOURCES}" - ) - endif() - # TODO: Use dependencies variable instead if(CMAKE_GENERATOR STREQUAL "Xcode") set(ELEMENTAL_PATH "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug/Elemental.framework") @@ -234,6 +228,23 @@ function(configure_project_package target_name install_folder) XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY "YES" XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY "YES" ) + + foreach(res IN LISTS ARG_RESOURCES) + get_filename_component(file_name "${res}" NAME) + get_filename_component(full_path "${res}" ABSOLUTE) + get_filename_component(file_dir "${full_path}" DIRECTORY) + + set(resource_root "${CMAKE_CURRENT_BINARY_DIR}/Data") + + file(RELATIVE_PATH relative_dir "${resource_root}" "${file_dir}") + + set_source_files_properties("${full_path}" + PROPERTIES MACOSX_PACKAGE_LOCATION "${relative_dir}" + ) + + # Attach file to the target so Xcode knows to include it + target_sources(${target_name} PRIVATE "${full_path}") + endforeach() else() add_custom_target(CopyFrameworkFolder${target_name} ALL) @@ -254,6 +265,38 @@ function(configure_project_package target_name install_folder) COMMENT "Copying ${dependency} framework folder to destination" ) endforeach() + + if(NOT resources_length EQUAL 0) + set(output_folder "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${target_name}.app/Contents/") + + foreach(file IN LISTS ARG_RESOURCES) + get_filename_component(file_name "${file}" NAME) + get_filename_component(full_path "${file}" ABSOLUTE) + get_filename_component(file_dir "${full_path}" DIRECTORY) + + set(resource_root "${CMAKE_CURRENT_BINARY_DIR}/Data") + + file(RELATIVE_PATH relative_dir "${resource_root}" "${file_dir}") + set(output_subdir "${output_folder}/Resources/${relative_dir}") + file(MAKE_DIRECTORY "${output_subdir}") + + set(output_file "${output_subdir}/${file_name}") + + add_custom_command( + OUTPUT "${output_file}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${output_folder}/Resources" + COMMAND ${CMAKE_COMMAND} -E copy "${full_path}" "${output_file}" + DEPENDS "${full_path}" + COMMENT "Copying and checking resource file ${file_name}" + ) + + list(APPEND output_files "${output_file}") + endforeach() + + add_custom_target(CopyResources${target_name} ALL DEPENDS ${output_files}) + add_dependencies(CopyResources${target_name} ShaderCompiler) + add_dependencies(${target_name} CopyResources${target_name}) + endif() add_dependencies(${target_name} CopyFrameworkFolder${target_name}) endif() From f4cd601240f45c51315d23c51817cfccbc940e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 6 Jan 2025 15:12:42 +0100 Subject: [PATCH 56/93] Put defines into a separate config file for metal --- .../Apple/Graphics/MetalCommandList.cpp | 4 +--- src/Elemental/Apple/Graphics/MetalConfig.h | 18 ++++++++++++++++++ .../Apple/Graphics/MetalGraphicsDevice.cpp | 4 ++-- .../Apple/Graphics/MetalGraphicsDevice.h | 4 ---- src/Elemental/Apple/Graphics/MetalResource.cpp | 5 +---- src/Elemental/Apple/Graphics/MetalShader.cpp | 4 +--- .../Apple/Graphics/MetalSwapChain.cpp | 3 +-- 7 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 src/Elemental/Apple/Graphics/MetalConfig.h diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index edc9dabb..ee24446e 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -1,12 +1,10 @@ #include "MetalCommandList.h" +#include "MetalConfig.h" #include "MetalGraphicsDevice.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" -#define METAL_MAX_COMMANDQUEUES 10u -#define METAL_MAX_COMMANDLISTS 64u - SystemDataPool metalCommandQueuePool; SystemDataPool metalCommandListPool; diff --git a/src/Elemental/Apple/Graphics/MetalConfig.h b/src/Elemental/Apple/Graphics/MetalConfig.h new file mode 100644 index 00000000..ca14cf24 --- /dev/null +++ b/src/Elemental/Apple/Graphics/MetalConfig.h @@ -0,0 +1,18 @@ +#pragma onc + +#define METAL_MEMORY_ARENA 128 * 1024 * 1024 +#define METAL_READBACK_MEMORY_ARENA 32 * 1024 * 1024 + +#define METAL_MAX_DEVICES 10u + +#define METAL_MAX_COMMANDQUEUES 10u +#define METAL_MAX_COMMANDLISTS 64u + +#define METAL_MAX_GRAPHICSHEAP 32 +#define METAL_MAX_RESOURCES 1000000 + +#define METAL_MAX_SWAPCHAINS 10u + +#define METAL_MAX_LIBRARIES UINT16_MAX +#define METAL_MAX_PIPELINESTATES UINT16_MAX + diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp index 58495cee..70267581 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp @@ -1,10 +1,10 @@ #include "MetalGraphicsDevice.h" +#include "MetalConfig.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemLogging.h" #include "SystemMemory.h" - struct MetalArgumentBufferDescriptor { uint64_t BufferAddress; @@ -115,7 +115,7 @@ void InitMetalGraphicsDeviceMemory() if (!MetalGraphicsMemoryArena.Storage) { // TODO: To Review - MetalGraphicsMemoryArena = SystemAllocateMemoryArena(128 * 1024 * 1024); + MetalGraphicsMemoryArena = SystemAllocateMemoryArena(METAL_MEMORY_ARENA); metalGraphicsDevicePool = SystemCreateDataPool(MetalGraphicsMemoryArena, METAL_MAX_DEVICES); InitMetal(); diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h index ad23d722..e01809bd 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h @@ -4,10 +4,6 @@ #include "Elemental.h" #include "SystemMemory.h" -#define METAL_MAX_DEVICES 10u -// TODO: Increase to 1.000.000? -#define METAL_MAX_RESOURCES 500000 - struct MetalArgumentBufferStorage; struct MetalArgumentBuffer diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index c8650d4f..ee24825f 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -1,4 +1,5 @@ #include "MetalResource.h" +#include "MetalConfig.h" #include "MetalGraphicsDevice.h" #include "MetalCommandList.h" #include "Graphics/UploadBufferPool.h" @@ -8,10 +9,6 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -// TODO: Move that to config header like DirectX12 -#define METAL_MAX_GRAPHICSHEAP 32 -#define METAL_READBACK_MEMORY_ARENA 32 * 1024 * 1024 - SystemDataPool metalGraphicsHeapPool; SystemDataPool metalResourcePool; Span metalResourceDescriptorInfos; diff --git a/src/Elemental/Apple/Graphics/MetalShader.cpp b/src/Elemental/Apple/Graphics/MetalShader.cpp index ac01a8d2..34c1a09e 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.cpp +++ b/src/Elemental/Apple/Graphics/MetalShader.cpp @@ -1,4 +1,5 @@ #include "MetalShader.h" +#include "MetalConfig.h" #include "MetalGraphicsDevice.h" #include "MetalCommandList.h" #include "MetalResource.h" @@ -10,9 +11,6 @@ #include "SystemLogging.h" #include "SystemMemory.h" -#define METAL_MAX_LIBRARIES UINT16_MAX -#define METAL_MAX_PIPELINESTATES UINT16_MAX - struct MetalShaderFunctionData { NS::SharedPtr Function; diff --git a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp index b5122738..809f3d16 100644 --- a/src/Elemental/Apple/Graphics/MetalSwapChain.cpp +++ b/src/Elemental/Apple/Graphics/MetalSwapChain.cpp @@ -1,4 +1,5 @@ #include "MetalSwapChain.h" +#include "MetalConfig.h" #include "MetalCommandList.h" #include "MetalGraphicsDevice.h" #include "MetalResource.h" @@ -27,8 +28,6 @@ struct Test #include "../UIKitWindow.h" #endif -#define METAL_MAX_SWAPCHAINS 10u - SystemDataPool metalSwapChainPool; void InitMetalSwapChainMemory() From d4d2bacff63cf53ccae54857d8dfb23c58d420c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Mon, 6 Jan 2025 15:35:20 +0100 Subject: [PATCH 57/93] Remove dotnet bindings --- LICENSE | 2 +- bindings/dotnet/Directory.Build.props | 31 --- bindings/dotnet/Directory.Packages.props | 10 - bindings/dotnet/Elemental.sln | 84 -------- .../Elemental.Tools/Elemental.Tools.nuspec | 16 -- .../dotnet/publish/Elemental/Elemental.nuspec | 16 -- .../samples/01-HelloWorld/HelloWorld.csproj | 15 -- .../dotnet/samples/01-HelloWorld/Program.cs | 20 -- .../samples/02-HelloWindow/HelloWindow.csproj | 16 -- .../dotnet/samples/02-HelloWindow/Program.cs | 38 ---- .../03-HelloTriangle/HelloTriangle.csproj | 28 --- .../samples/03-HelloTriangle/Program.cs | 165 -------------- .../samples/03-HelloTriangle/Triangle.hlsl | 60 ------ .../samples/03-HelloTriangle/Triangle.metal | 117 ---------- .../HelloTriangleMin.csproj | 17 -- .../samples/03-HelloTriangleMin/Program.cs | 106 --------- .../samples/04-HelloInputs/HelloInputs.csproj | 28 --- .../dotnet/samples/04-HelloInputs/Program.cs | 203 ------------------ .../samples/04-HelloInputs/Triangle.hlsl | 128 ----------- .../samples/04-HelloInputs/Triangle.metal | 132 ------------ .../xx-MultiThread/HelloTriangle.csproj | 16 -- .../dotnet/samples/xx-MultiThread/Program.cs | 138 ------------ .../Elemental.Tools/Elemental.Tools.csproj | 50 ----- .../dotnet/src/Elemental.Tools/Globals.cs | 6 - .../src/Elemental.Tools/IShaderCompiler.cs | 37 ---- .../Elemental.Tools/PlatformServiceInterop.cs | 26 --- .../ShaderCompilationOptions.cs | 22 -- .../Elemental.Tools/ShaderCompilerInput.cs | 61 ------ .../Elemental.Tools/ShaderCompilerLogEntry.cs | 19 -- .../ShaderCompilerLogEntryType.cs | 22 -- .../Elemental.Tools/ShaderCompilerOptions.cs | 42 ---- .../Elemental.Tools/ShaderCompilerResult.cs | 113 ---------- .../src/Elemental.Tools/ShaderLanguage.cs | 37 ---- .../src/Elemental/ApplicationHandler.cs | 6 - .../src/Elemental/ApplicationService.cs | 177 --------------- .../Elemental/ApplicationServiceInterop.cs | 55 ----- .../dotnet/src/Elemental/ConsoleLogHandler.cs | 37 ---- .../dotnet/src/Elemental/Elemental.csproj | 50 ----- bindings/dotnet/src/Elemental/Globals.cs | 7 - .../src/Elemental/IApplicationService.cs | 76 ------- bindings/dotnet/src/Elemental/LogHandler.cs | 71 ------ .../src/Elemental/LogMessageCategory.cs | 32 --- .../dotnet/src/Elemental/LogMessageType.cs | 22 -- bindings/dotnet/src/Elemental/Platform.cs | 27 --- .../src/Elemental/RunApplicationParameters.cs | 39 ---- bindings/dotnet/src/Elemental/SpanUnsafe.cs | 7 - bindings/dotnet/src/Elemental/SystemInfo.cs | 32 --- bindings/dotnet/src/Elemental/Window.cs | 17 -- .../src/Elemental/WindowCursorPosition.cs | 11 - .../dotnet/src/Elemental/WindowOptions.cs | 46 ---- bindings/dotnet/src/Elemental/WindowSize.cs | 27 --- bindings/dotnet/src/Elemental/WindowState.cs | 27 --- .../src/Elemental_REF/ApplicationHandler.cs | 6 - .../src/Elemental_REF/ApplicationService.cs | 177 --------------- .../ApplicationServiceInterop.cs | 55 ----- .../src/Elemental_REF/ConsoleLogHandler.cs | 37 ---- bindings/dotnet/src/Elemental_REF/Globals.cs | 7 - .../src/Elemental_REF/IApplicationService.cs | 76 ------- .../dotnet/src/Elemental_REF/LogHandler.cs | 71 ------ .../src/Elemental_REF/LogMessageCategory.cs | 32 --- .../src/Elemental_REF/LogMessageType.cs | 22 -- bindings/dotnet/src/Elemental_REF/Platform.cs | 27 --- .../Elemental_REF/RunApplicationParameters.cs | 39 ---- .../dotnet/src/Elemental_REF/SpanUnsafe.cs | 7 - .../dotnet/src/Elemental_REF/SystemInfo.cs | 32 --- bindings/dotnet/src/Elemental_REF/Window.cs | 17 -- .../src/Elemental_REF/WindowCursorPosition.cs | 11 - .../dotnet/src/Elemental_REF/WindowOptions.cs | 46 ---- .../dotnet/src/Elemental_REF/WindowSize.cs | 27 --- .../dotnet/src/Elemental_REF/WindowState.cs | 27 --- 70 files changed, 1 insertion(+), 3302 deletions(-) delete mode 100644 bindings/dotnet/Directory.Build.props delete mode 100644 bindings/dotnet/Directory.Packages.props delete mode 100644 bindings/dotnet/Elemental.sln delete mode 100644 bindings/dotnet/publish/Elemental.Tools/Elemental.Tools.nuspec delete mode 100644 bindings/dotnet/publish/Elemental/Elemental.nuspec delete mode 100644 bindings/dotnet/samples/01-HelloWorld/HelloWorld.csproj delete mode 100644 bindings/dotnet/samples/01-HelloWorld/Program.cs delete mode 100644 bindings/dotnet/samples/02-HelloWindow/HelloWindow.csproj delete mode 100644 bindings/dotnet/samples/02-HelloWindow/Program.cs delete mode 100644 bindings/dotnet/samples/03-HelloTriangle/HelloTriangle.csproj delete mode 100644 bindings/dotnet/samples/03-HelloTriangle/Program.cs delete mode 100644 bindings/dotnet/samples/03-HelloTriangle/Triangle.hlsl delete mode 100644 bindings/dotnet/samples/03-HelloTriangle/Triangle.metal delete mode 100644 bindings/dotnet/samples/03-HelloTriangleMin/HelloTriangleMin.csproj delete mode 100644 bindings/dotnet/samples/03-HelloTriangleMin/Program.cs delete mode 100644 bindings/dotnet/samples/04-HelloInputs/HelloInputs.csproj delete mode 100644 bindings/dotnet/samples/04-HelloInputs/Program.cs delete mode 100644 bindings/dotnet/samples/04-HelloInputs/Triangle.hlsl delete mode 100644 bindings/dotnet/samples/04-HelloInputs/Triangle.metal delete mode 100644 bindings/dotnet/samples/xx-MultiThread/HelloTriangle.csproj delete mode 100644 bindings/dotnet/samples/xx-MultiThread/Program.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/Elemental.Tools.csproj delete mode 100644 bindings/dotnet/src/Elemental.Tools/Globals.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/IShaderCompiler.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/PlatformServiceInterop.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilationOptions.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilerInput.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntry.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntryType.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilerOptions.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderCompilerResult.cs delete mode 100644 bindings/dotnet/src/Elemental.Tools/ShaderLanguage.cs delete mode 100644 bindings/dotnet/src/Elemental/ApplicationHandler.cs delete mode 100644 bindings/dotnet/src/Elemental/ApplicationService.cs delete mode 100644 bindings/dotnet/src/Elemental/ApplicationServiceInterop.cs delete mode 100644 bindings/dotnet/src/Elemental/ConsoleLogHandler.cs delete mode 100644 bindings/dotnet/src/Elemental/Elemental.csproj delete mode 100644 bindings/dotnet/src/Elemental/Globals.cs delete mode 100644 bindings/dotnet/src/Elemental/IApplicationService.cs delete mode 100644 bindings/dotnet/src/Elemental/LogHandler.cs delete mode 100644 bindings/dotnet/src/Elemental/LogMessageCategory.cs delete mode 100644 bindings/dotnet/src/Elemental/LogMessageType.cs delete mode 100644 bindings/dotnet/src/Elemental/Platform.cs delete mode 100644 bindings/dotnet/src/Elemental/RunApplicationParameters.cs delete mode 100644 bindings/dotnet/src/Elemental/SpanUnsafe.cs delete mode 100644 bindings/dotnet/src/Elemental/SystemInfo.cs delete mode 100644 bindings/dotnet/src/Elemental/Window.cs delete mode 100644 bindings/dotnet/src/Elemental/WindowCursorPosition.cs delete mode 100644 bindings/dotnet/src/Elemental/WindowOptions.cs delete mode 100644 bindings/dotnet/src/Elemental/WindowSize.cs delete mode 100644 bindings/dotnet/src/Elemental/WindowState.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/ApplicationHandler.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/ApplicationService.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/ApplicationServiceInterop.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/ConsoleLogHandler.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/Globals.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/IApplicationService.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/LogHandler.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/LogMessageCategory.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/LogMessageType.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/Platform.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/RunApplicationParameters.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/SpanUnsafe.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/SystemInfo.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/Window.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/WindowCursorPosition.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/WindowOptions.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/WindowSize.cs delete mode 100644 bindings/dotnet/src/Elemental_REF/WindowState.cs diff --git a/LICENSE b/LICENSE index a0a4bc38..bdfc3bbe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023-2024 Double Buffer SRL +Copyright (c) 2023-2025 Double Buffer SRL Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bindings/dotnet/Directory.Build.props b/bindings/dotnet/Directory.Build.props deleted file mode 100644 index e403c4a0..00000000 --- a/bindings/dotnet/Directory.Build.props +++ /dev/null @@ -1,31 +0,0 @@ - - - - net9.0 - preview - enable - enable - true - true - - - - true - CS1668;CA5393;CA1711;CA1420 - - - - latest - All - All - All - All - All - All - All - All - All - All - - - diff --git a/bindings/dotnet/Directory.Packages.props b/bindings/dotnet/Directory.Packages.props deleted file mode 100644 index 2cbaa763..00000000 --- a/bindings/dotnet/Directory.Packages.props +++ /dev/null @@ -1,10 +0,0 @@ - - - true - - - - - - - diff --git a/bindings/dotnet/Elemental.sln b/bindings/dotnet/Elemental.sln deleted file mode 100644 index b67e6fea..00000000 --- a/bindings/dotnet/Elemental.sln +++ /dev/null @@ -1,84 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1F23B2BD-4FB1-4466-B5FA-F03D6390A8D2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elemental", "src\Elemental\Elemental.csproj", "{B68362C7-510F-4E5F-AE3B-BF5BE481B8EB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elemental.Common", "src\Elemental.Common\Elemental.Common.csproj", "{63930D32-EE2F-4E48-8AFF-FB5A90807946}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elemental.Tools", "src\Elemental.Tools\Elemental.Tools.csproj", "{88001C17-BA54-4869-8E51-A8787365C400}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elemental.SourceGenerators", "src\Elemental.SourceGenerators\Elemental.SourceGenerators.csproj", "{3A9C7DBA-AB71-47E5-855D-9223C5B4897E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "..\..\samples\dotnet\01-HelloWorld\HelloWorld.csproj", "{5E78BF43-09F0-4FFD-B073-644EA9531AAA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F7455345-C90D-405C-BD55-0B9BC7BC7CDE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "samples\01-HelloWorld\HelloWorld.csproj", "{463AC234-64AB-411E-B4B1-19FC384192D8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWindow", "samples\02-HelloWindow\HelloWindow.csproj", "{92FD8004-B341-426D-8251-782BB27AE9EE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloTriangle", "samples\03-HelloTriangle\HelloTriangle.csproj", "{4567AC5D-0B65-4C07-8F22-EE10DD553920}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloInputs", "samples\04-HelloInputs\HelloInputs.csproj", "{D30D019A-E8F2-4E2D-987B-225E6EA532E6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B68362C7-510F-4E5F-AE3B-BF5BE481B8EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B68362C7-510F-4E5F-AE3B-BF5BE481B8EB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B68362C7-510F-4E5F-AE3B-BF5BE481B8EB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B68362C7-510F-4E5F-AE3B-BF5BE481B8EB}.Release|Any CPU.Build.0 = Release|Any CPU - {63930D32-EE2F-4E48-8AFF-FB5A90807946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {63930D32-EE2F-4E48-8AFF-FB5A90807946}.Debug|Any CPU.Build.0 = Debug|Any CPU - {63930D32-EE2F-4E48-8AFF-FB5A90807946}.Release|Any CPU.ActiveCfg = Release|Any CPU - {63930D32-EE2F-4E48-8AFF-FB5A90807946}.Release|Any CPU.Build.0 = Release|Any CPU - {88001C17-BA54-4869-8E51-A8787365C400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88001C17-BA54-4869-8E51-A8787365C400}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88001C17-BA54-4869-8E51-A8787365C400}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88001C17-BA54-4869-8E51-A8787365C400}.Release|Any CPU.Build.0 = Release|Any CPU - {3A9C7DBA-AB71-47E5-855D-9223C5B4897E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3A9C7DBA-AB71-47E5-855D-9223C5B4897E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3A9C7DBA-AB71-47E5-855D-9223C5B4897E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3A9C7DBA-AB71-47E5-855D-9223C5B4897E}.Release|Any CPU.Build.0 = Release|Any CPU - {5E78BF43-09F0-4FFD-B073-644EA9531AAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E78BF43-09F0-4FFD-B073-644EA9531AAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E78BF43-09F0-4FFD-B073-644EA9531AAA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E78BF43-09F0-4FFD-B073-644EA9531AAA}.Release|Any CPU.Build.0 = Release|Any CPU - {463AC234-64AB-411E-B4B1-19FC384192D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {463AC234-64AB-411E-B4B1-19FC384192D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {463AC234-64AB-411E-B4B1-19FC384192D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {463AC234-64AB-411E-B4B1-19FC384192D8}.Release|Any CPU.Build.0 = Release|Any CPU - {92FD8004-B341-426D-8251-782BB27AE9EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {92FD8004-B341-426D-8251-782BB27AE9EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {92FD8004-B341-426D-8251-782BB27AE9EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {92FD8004-B341-426D-8251-782BB27AE9EE}.Release|Any CPU.Build.0 = Release|Any CPU - {4567AC5D-0B65-4C07-8F22-EE10DD553920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4567AC5D-0B65-4C07-8F22-EE10DD553920}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4567AC5D-0B65-4C07-8F22-EE10DD553920}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4567AC5D-0B65-4C07-8F22-EE10DD553920}.Release|Any CPU.Build.0 = Release|Any CPU - {D30D019A-E8F2-4E2D-987B-225E6EA532E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D30D019A-E8F2-4E2D-987B-225E6EA532E6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D30D019A-E8F2-4E2D-987B-225E6EA532E6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D30D019A-E8F2-4E2D-987B-225E6EA532E6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {B68362C7-510F-4E5F-AE3B-BF5BE481B8EB} = {1F23B2BD-4FB1-4466-B5FA-F03D6390A8D2} - {63930D32-EE2F-4E48-8AFF-FB5A90807946} = {1F23B2BD-4FB1-4466-B5FA-F03D6390A8D2} - {88001C17-BA54-4869-8E51-A8787365C400} = {1F23B2BD-4FB1-4466-B5FA-F03D6390A8D2} - {3A9C7DBA-AB71-47E5-855D-9223C5B4897E} = {1F23B2BD-4FB1-4466-B5FA-F03D6390A8D2} - {463AC234-64AB-411E-B4B1-19FC384192D8} = {F7455345-C90D-405C-BD55-0B9BC7BC7CDE} - {92FD8004-B341-426D-8251-782BB27AE9EE} = {F7455345-C90D-405C-BD55-0B9BC7BC7CDE} - {4567AC5D-0B65-4C07-8F22-EE10DD553920} = {F7455345-C90D-405C-BD55-0B9BC7BC7CDE} - {D30D019A-E8F2-4E2D-987B-225E6EA532E6} = {F7455345-C90D-405C-BD55-0B9BC7BC7CDE} - EndGlobalSection -EndGlobal diff --git a/bindings/dotnet/publish/Elemental.Tools/Elemental.Tools.nuspec b/bindings/dotnet/publish/Elemental.Tools/Elemental.Tools.nuspec deleted file mode 100644 index 3a44feda..00000000 --- a/bindings/dotnet/publish/Elemental.Tools/Elemental.Tools.nuspec +++ /dev/null @@ -1,16 +0,0 @@ - - - - Elemental.Tools - #VERSION# - Double Buffer - Elemental Tools contains tools for Elemental, a portable low-level game platform abstraction library for .NET 7+ that targets only next-gen features. - Copyright (c) Double Buffer SRL 2023 - https://github.com/double-buffer/elemental - - - MIT - gamedev;graphics;windows;macos; - - - \ No newline at end of file diff --git a/bindings/dotnet/publish/Elemental/Elemental.nuspec b/bindings/dotnet/publish/Elemental/Elemental.nuspec deleted file mode 100644 index 592eec5c..00000000 --- a/bindings/dotnet/publish/Elemental/Elemental.nuspec +++ /dev/null @@ -1,16 +0,0 @@ - - - - Elemental - #VERSION# - Double Buffer - Elemental is a portable low-level game platform abstraction library for .NET 7+ that targets only next-gen features. - Copyright (c) Double Buffer SRL 2023 - https://github.com/double-buffer/elemental - - - MIT - gamedev;graphics;windows;macos; - - - \ No newline at end of file diff --git a/bindings/dotnet/samples/01-HelloWorld/HelloWorld.csproj b/bindings/dotnet/samples/01-HelloWorld/HelloWorld.csproj deleted file mode 100644 index 79889a52..00000000 --- a/bindings/dotnet/samples/01-HelloWorld/HelloWorld.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - True - - - - - - - - - - diff --git a/bindings/dotnet/samples/01-HelloWorld/Program.cs b/bindings/dotnet/samples/01-HelloWorld/Program.cs deleted file mode 100644 index 20fe0009..00000000 --- a/bindings/dotnet/samples/01-HelloWorld/Program.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Elemental; - -var counter = 0; - -var applicationService = new ApplicationService(); -applicationService.ConfigureLogHandler(DefaultLogHandlers.ConsoleLogHandler); - -using var application = applicationService.CreateApplication("Hello World"u8); - -applicationService.RunApplication(application, (status) => -{ - if (counter > 10 || status != ApplicationStatus.Active) - { - return false; - } - - Console.WriteLine($"Hello World {counter}!"); - counter++; - return true; -}); diff --git a/bindings/dotnet/samples/02-HelloWindow/HelloWindow.csproj b/bindings/dotnet/samples/02-HelloWindow/HelloWindow.csproj deleted file mode 100644 index 444bd662..00000000 --- a/bindings/dotnet/samples/02-HelloWindow/HelloWindow.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - True - - - - $(AssemblyName).app/Contents/MacOS - - - - - - - diff --git a/bindings/dotnet/samples/02-HelloWindow/Program.cs b/bindings/dotnet/samples/02-HelloWindow/Program.cs deleted file mode 100644 index 81bbd04a..00000000 --- a/bindings/dotnet/samples/02-HelloWindow/Program.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Elemental; - -var applicationService = new ApplicationService(); -applicationService.ConfigureLogHandler(DefaultLogHandlers.ConsoleLogHandler); - -// TODO: Scenario with no payloads -applicationService.RunApplication(new () -{ - ApplicationName = "Hello Window"u8, - InitHandler = InitSample, - FreeHandler = FreeSample, - Payload = new TestPayload - { - Test = 28 - } -}); - -void InitSample(ref TestPayload payload) -{ - var systemInfo = applicationService.GetSystemInfo(); - - Console.WriteLine($"Path: {System.Text.Encoding.UTF8.GetString(systemInfo.ApplicationPath)}"); - - payload.Window = applicationService.CreateWindow(new WindowOptions { Title = "Test"u8 }); - Console.WriteLine($"Test: {payload.Test}"); -} - -void FreeSample(ref TestPayload payload) -{ - payload.Window.Dispose(); - Console.WriteLine("Exit Sample"); -} - -record struct TestPayload -{ - public Window Window { get; set; } - public int Test { get; set; } -} diff --git a/bindings/dotnet/samples/03-HelloTriangle/HelloTriangle.csproj b/bindings/dotnet/samples/03-HelloTriangle/HelloTriangle.csproj deleted file mode 100644 index 7d129c49..00000000 --- a/bindings/dotnet/samples/03-HelloTriangle/HelloTriangle.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - True - - - - $(AssemblyName).app/Contents/MacOS - - - - - - - - - - Triangle.metal - PreserveNewest - - - Triangle.hlsl - PreserveNewest - - - - diff --git a/bindings/dotnet/samples/03-HelloTriangle/Program.cs b/bindings/dotnet/samples/03-HelloTriangle/Program.cs deleted file mode 100644 index ec5f1e12..00000000 --- a/bindings/dotnet/samples/03-HelloTriangle/Program.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System.Diagnostics; -using System.Numerics; -using Elemental; -using Elemental.Graphics; -using Elemental.Tools; - -//TODO: HelloTriangle should be minimal, do another sample to demonstrate the debug functionality - -static void LogMessageHandler(LogMessageType messageType, LogMessageCategory category, string function, string message) -{ - var mainForegroundColor = messageType switch - { - LogMessageType.Warning => ConsoleColor.Yellow, - LogMessageType.Error => ConsoleColor.Red, - _ => ConsoleColor.Gray - }; - - Console.Write("["); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write($"{category}"); - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write("]"); - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write($" {function}"); - - Console.ForegroundColor = mainForegroundColor; - Console.WriteLine($" {message}"); - Console.ForegroundColor = ConsoleColor.Gray; -} - -using var applicationService = new NativeApplicationService(new() { LogMessageHandler = LogMessageHandler }); -using var graphicsService = new GraphicsService(new() { GraphicsDiagnostics = GraphicsDiagnostics.Debug }); - -using var application = applicationService.CreateApplication("Hello Window"); -using var window = applicationService.CreateWindow(application); - -var availableGraphicsDevices = graphicsService.GetAvailableGraphicsDevices(); -var selectedGraphicsDevice = availableGraphicsDevices[0]; - -// TODO: Do a separate sample to showcase the use of Native + Vulkan with 2 windows (if available) -foreach (var availableGraphicsDevice in availableGraphicsDevices) -{ - if (availableGraphicsDevice.GraphicsApi == GraphicsApi.Vulkan) - { - //selectedGraphicsDevice = availableGraphicsDevice; - } - - Console.WriteLine($"{availableGraphicsDevice}"); -} - -using var graphicsDevice = graphicsService.CreateGraphicsDevice(new() { DeviceId = selectedGraphicsDevice.DeviceId }); -using var commandQueue = graphicsService.CreateCommandQueue(graphicsDevice, CommandQueueType.Graphics); - -applicationService.SetWindowTitle(window, $"Hello Triangle! (GraphicsDevice: {graphicsService.GetGraphicsDeviceInfo(graphicsDevice)})"); - -using var swapChain = graphicsService.CreateSwapChain(window, commandQueue); -var currentRenderSize = applicationService.GetWindowRenderSize(window); - -// HACK: This is needed when we run the program from the root directory -Environment.CurrentDirectory = Path.GetDirectoryName(Environment.ProcessPath)!; - -using var shaderCompiler = new ShaderCompiler(); - -Console.WriteLine($"Can Compile Shader HLSL: {shaderCompiler.CanCompileShader(ShaderLanguage.Hlsl, selectedGraphicsDevice.GraphicsApi)}"); -Console.WriteLine($"Can Compile Shader Metal: {shaderCompiler.CanCompileShader(ShaderLanguage.Msl, selectedGraphicsDevice.GraphicsApi)}"); - -var shaderCode = File.ReadAllText("Triangle.hlsl"); - -// TODO: This is needed for Apple Metal because SPIRV-Cross doesn't yet support metal mesh shaders -var shaderCodeMetal = File.ReadAllText("Triangle.metal"); - -var meshShaderSourceType = selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal ? ShaderLanguage.Msl : ShaderLanguage.Hlsl; - -var shaderInputs = new ShaderCompilerInput[] -{ - new() { ShaderCode = selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal ? shaderCodeMetal : shaderCode, Stage = ShaderStage.MeshShader, EntryPoint = "MeshMain", ShaderLanguage = meshShaderSourceType }, - new() { ShaderCode = shaderCode, Stage = ShaderStage.PixelShader, EntryPoint = "PixelMain", ShaderLanguage = ShaderLanguage.Hlsl } -}; - -var shaderCompilationResults = shaderCompiler.CompileShaders(shaderInputs, selectedGraphicsDevice.GraphicsApi); - -// TODO: Avoid the array declaration -var shaderParts = new ShaderPart[shaderCompilationResults.Length]; - -for (var i = 0; i < shaderCompilationResults.Length; i++) -{ - var shaderCompilationResult = shaderCompilationResults[i]; - - foreach (var logEntry in shaderCompilationResult.LogEntries.Span) - { - Console.WriteLine($"{logEntry.Type}: {logEntry.Message}"); - } - - if (!shaderCompilationResult.IsSuccess) - { - return; - } - - shaderParts[i] = new() - { - Stage = shaderCompilationResult.Stage, - EntryPoint = shaderCompilationResult.EntryPoint, - Data = shaderCompilationResult.ShaderData, - MetaData = !(selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal && shaderCompilationResult.Stage == ShaderStage.MeshShader) ? shaderCompilationResult.MetaData : new ShaderMetaData[] - { - new ShaderMetaData { Type = ShaderMetaDataType.PushConstantsCount, Value = 1 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountX, Value = 32 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountY, Value = 1 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountZ, Value = 1 } - } - }; -} - -using var shader = graphicsService.CreateShader(graphicsDevice, shaderParts); - -var stopWatch = new Stopwatch(); -var frameTimer = new Stopwatch(); -var rotationY = 0.0f; - -applicationService.RunApplication(application, (status) => -{ - if (status.IsClosing) - { - return false; - } - - var renderSize = applicationService.GetWindowRenderSize(window); - - if (renderSize != currentRenderSize) - { - Console.WriteLine($"Resize SwapChain to: {renderSize}"); - graphicsService.ResizeSwapChain(swapChain, renderSize.Width, renderSize.Height); - currentRenderSize = renderSize; - } - - stopWatch.Restart(); - graphicsService.WaitForSwapChainOnCpu(swapChain); - stopWatch.Stop(); - - //Console.WriteLine($"Wait for swapchain {stopWatch.Elapsed.TotalMilliseconds}"); - - rotationY += (float)frameTimer.Elapsed.TotalSeconds; - frameTimer.Restart(); - - using var commandList = graphicsService.CreateCommandList(commandQueue); - - using var backbufferTexture = graphicsService.GetSwapChainBackBufferTexture(swapChain); - graphicsService.BeginRenderPass(commandList, new() { RenderTarget0 = new() { Texture = backbufferTexture, ClearColor = new Vector4(0.0f, 1.0f, 1.0f, 1.0f) } }); - - graphicsService.SetShader(commandList, shader); - graphicsService.SetShaderConstants(commandList, 0, ref rotationY); - graphicsService.DispatchMesh(commandList, 1, 1, 1); - - graphicsService.EndRenderPass(commandList); - graphicsService.CommitCommandList(commandList); - graphicsService.ExecuteCommandList(commandQueue, commandList); - - stopWatch.Restart(); - graphicsService.PresentSwapChain(swapChain); - stopWatch.Stop(); - - //Console.WriteLine($"Present swapchain {stopWatch.Elapsed.TotalMilliseconds}"); - return true; -}); diff --git a/bindings/dotnet/samples/03-HelloTriangle/Triangle.hlsl b/bindings/dotnet/samples/03-HelloTriangle/Triangle.hlsl deleted file mode 100644 index ed9afd1e..00000000 --- a/bindings/dotnet/samples/03-HelloTriangle/Triangle.hlsl +++ /dev/null @@ -1,60 +0,0 @@ -#define RootSignatureDef "RootFlags(0), RootConstants(num32BitConstants=1, b0)" - -struct ShaderParameters -{ - float RotationY; -}; - -[[vk::push_constant]] -ShaderParameters parameters : register(b0); - -struct Vertex -{ - float3 Position; - float4 Color; -}; - -struct VertexOutput -{ - float4 Position: SV_Position; - float4 Color: TEXCOORD0; -}; - -static Vertex triangleVertices[] = -{ - { float3(-0.5, 0.5, 0.0), float4(1.0, 0.0, 0.0, 1.0) }, - { float3(0.5, 0.5, 0.0), float4(0.0, 1.0, 0.0, 1.0) }, - { float3(-0.5, -0.5, 0.0), float4(0.0, 0.0, 1.0, 1.0) } -}; - -float3 Transform(float3 position, float rotationY) -{ - float cosY = cos(rotationY); - float sinY = sin(rotationY); - return float3(position.x * cosY - position.z * sinY, position.y, position.x * sinY + position.z * cosY + 0.5); -} - -[OutputTopology("triangle")] -[NumThreads(32, 1, 1)] -void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[3], out indices uint3 indices[1]) -{ - const uint meshVertexCount = 3; - - SetMeshOutputCounts(meshVertexCount, 1); - - if (groupThreadId < meshVertexCount) - { - vertices[groupThreadId].Position = float4(Transform(triangleVertices[groupThreadId].Position, parameters.RotationY), 1.0); - vertices[groupThreadId].Color = triangleVertices[groupThreadId].Color; - } - - if (groupThreadId == 0) - { - indices[groupThreadId] = uint3(0, 1, 2); - } -} - -float4 PixelMain(const VertexOutput input) : SV_Target0 -{ - return input.Color; -} diff --git a/bindings/dotnet/samples/03-HelloTriangle/Triangle.metal b/bindings/dotnet/samples/03-HelloTriangle/Triangle.metal deleted file mode 100644 index 4d35542d..00000000 --- a/bindings/dotnet/samples/03-HelloTriangle/Triangle.metal +++ /dev/null @@ -1,117 +0,0 @@ -using namespace metal; - -#include -#include - -struct VertexOutput -{ - float4 Position [[position]]; - float4 Color [[user(locn0)]]; -}; - -struct PrimitiveOutput -{ -}; - -struct ShaderParameters -{ - float RotationY; -}; - -constant float3 triangleVertices[] = -{ - float3(-0.5, 0.5, 0), - float3(0.5, 0.5, 0), - float3(-0.5, -0.5, 0) -}; - -constant float4 triangleColors[] = -{ - float4(1.0, 0.0, 0, 1), - float4(0.0, 1.0, 0, 1), - float4(0.0, 0.0, 1, 1) -}; - -constant uint8_t rectangleIndices[] = -{ - 0, 1, 2 -}; - -float3 rotate(float3 position, float pitch, float roll, float yaw) -{ - float cosa = cos(yaw); - float sina = sin(yaw); - - float cosb = cos(pitch); - float sinb = sin(pitch); - - float cosc = cos(roll); - float sinc = sin(roll); - - float Axx = cosa*cosb; - float Axy = cosa*sinb*sinc - sina*cosc; - float Axz = cosa*sinb*cosc + sina*sinc; - - float Ayx = sina*cosb; - float Ayy = sina*sinb*sinc + cosa*cosc; - float Ayz = sina*sinb*cosc - cosa*sinc; - - float Azx = -sinb; - float Azy = cosb*sinc; - float Azz = cosb*cosc; - - float px = position.x; - float py = position.y; - float pz = position.z; - - float3 result; - - result.x = Axx*px + Axy*py + Axz*pz; - result.y = Ayx*px + Ayy*py + Ayz*pz; - result.z = Azx*px + Azy*py + Azz*pz; - - return result; -} - -constant uint32_t meshVertexCount = 3; -constant uint32_t meshPrimitiveCount = 1; - -using AAPLTriangleMeshType = metal::mesh; - -/// The mesh stage function that generates a triangle mesh. -[[mesh, max_total_threads_per_threadgroup(126)]] -void MeshMain(AAPLTriangleMeshType output, - uint32_t groupThreadId [[thread_index_in_threadgroup]], - uint32_t groupId [[threadgroup_position_in_grid]], - device const ShaderParameters ¶meters [[buffer(0)]]) -{ - - - output.set_primitive_count(meshPrimitiveCount); - - if (groupThreadId < meshVertexCount) - { - VertexOutput vertexOutput = {}; - - float3 position = triangleVertices[groupThreadId]; - - position = rotate(position, parameters.RotationY, 0, 0); - position.z = 0.5; - - vertexOutput.Position = float4(position, 1.0); - vertexOutput.Color = triangleColors[groupThreadId]; - - output.set_vertex(groupThreadId, vertexOutput); - } - - if (groupThreadId < meshPrimitiveCount) - { - PrimitiveOutput primitiveOutput; - output.set_primitive(groupThreadId, primitiveOutput); - - uint i = (3*groupThreadId); - output.set_index(i+0, rectangleIndices[i+0]); - output.set_index(i+1, rectangleIndices[i+1]); - output.set_index(i+2, rectangleIndices[i+2]); - } -} \ No newline at end of file diff --git a/bindings/dotnet/samples/03-HelloTriangleMin/HelloTriangleMin.csproj b/bindings/dotnet/samples/03-HelloTriangleMin/HelloTriangleMin.csproj deleted file mode 100644 index 0a7be362..00000000 --- a/bindings/dotnet/samples/03-HelloTriangleMin/HelloTriangleMin.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Exe - True - - - - $(AssemblyName).app/Contents/MacOS - - - - - - - - diff --git a/bindings/dotnet/samples/03-HelloTriangleMin/Program.cs b/bindings/dotnet/samples/03-HelloTriangleMin/Program.cs deleted file mode 100644 index bd18ca46..00000000 --- a/bindings/dotnet/samples/03-HelloTriangleMin/Program.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Numerics; -using Elemental; -using Elemental.Graphics; -using Elemental.Tools; - -var applicationService = new NativeApplicationService(); -using var graphicsService = new GraphicsService(); - -using var application = applicationService.CreateApplication("Hello Window"); -using var window = applicationService.CreateWindow(application); - -using var graphicsDevice = graphicsService.CreateGraphicsDevice(); -using var commandQueue = graphicsService.CreateCommandQueue(graphicsDevice, CommandQueueType.Graphics); -using var swapChain = graphicsService.CreateSwapChain(window, commandQueue); -var currentRenderSize = applicationService.GetWindowRenderSize(window); - -var shaderCode = - """ - #define RootSignatureDef "RootFlags(0)" - - struct Vertex { float3 Position; float4 Color; }; - struct VertexOutput { float4 Position: SV_Position; float4 Color: TEXCOORD0; }; - - static Vertex triangleVertices[] = - { - { float3(-0.5, 0.5, 0.0), float4(1.0, 0.0, 0.0, 1.0) }, - { float3(0.5, 0.5, 0.0), float4(0.0, 1.0, 0.0, 1.0) }, - { float3(-0.5, -0.5, 0.0), float4(0.0, 0.0, 1.0, 1.0) } - }; - - [OutputTopology("triangle")] - [NumThreads(32, 1, 1)] - void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[3], out indices uint3 indices[1]) - { - const uint meshVertexCount = 3; - - SetMeshOutputCounts(meshVertexCount, 1); - - if (groupThreadId < meshVertexCount) - { - vertices[groupThreadId].Position = float4(triangleVertices[groupThreadId].Position, 1.0); - vertices[groupThreadId].Color = triangleVertices[groupThreadId].Color; - } - - if (groupThreadId == 0) - { - indices[groupThreadId] = uint3(0, 1, 2); - } - } - - float4 PixelMain(const VertexOutput input) : SV_Target0 - { - return input.Color; - } - """; - -using var shaderCompiler = new ShaderCompiler(); - -var shaderInputs = new ShaderCompilerInput[] -{ - new() { ShaderCode = shaderCode, Stage = ShaderStage.MeshShader, EntryPoint = "MeshMain", ShaderLanguage = ShaderLanguage.Hlsl }, - new() { ShaderCode = shaderCode, Stage = ShaderStage.PixelShader, EntryPoint = "PixelMain", ShaderLanguage = ShaderLanguage.Hlsl } -}; - -var shaderCompilationResults = shaderCompiler.CompileShaders(shaderInputs, graphicsService.GetGraphicsDeviceInfo(graphicsDevice).GraphicsApi); -var shaderParts = new ShaderPart[shaderCompilationResults.Length]; - -for (var i = 0; i < shaderCompilationResults.Length; i++) -{ - shaderParts[i] = new() - { - Stage = shaderCompilationResults[i].Stage, - EntryPoint = shaderCompilationResults[i].EntryPoint, - Data = shaderCompilationResults[i].ShaderData, - MetaData = shaderCompilationResults[i].MetaData - }; -} - -using var shader = graphicsService.CreateShader(graphicsDevice, shaderParts); - -applicationService.RunApplication(application, (status) => -{ - if (status.IsClosing) return false; - - var renderSize = applicationService.GetWindowRenderSize(window); - - if (renderSize != currentRenderSize) - { - graphicsService.ResizeSwapChain(swapChain, renderSize.Width, renderSize.Height); - currentRenderSize = renderSize; - } - - graphicsService.WaitForSwapChainOnCpu(swapChain); - - using var commandList = graphicsService.CreateCommandList(commandQueue); - using var backbufferTexture = graphicsService.GetSwapChainBackBufferTexture(swapChain); - graphicsService.BeginRenderPass(commandList, new() { RenderTarget0 = new() { Texture = backbufferTexture, ClearColor = new Vector4(0.0f, 1.0f, 1.0f, 1.0f) } }); - graphicsService.SetShader(commandList, shader); - graphicsService.DispatchMesh(commandList, 1, 1, 1); - graphicsService.EndRenderPass(commandList); - graphicsService.CommitCommandList(commandList); - graphicsService.ExecuteCommandList(commandQueue, commandList); - - graphicsService.PresentSwapChain(swapChain); - return true; -}); \ No newline at end of file diff --git a/bindings/dotnet/samples/04-HelloInputs/HelloInputs.csproj b/bindings/dotnet/samples/04-HelloInputs/HelloInputs.csproj deleted file mode 100644 index 7d129c49..00000000 --- a/bindings/dotnet/samples/04-HelloInputs/HelloInputs.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - True - - - - $(AssemblyName).app/Contents/MacOS - - - - - - - - - - Triangle.metal - PreserveNewest - - - Triangle.hlsl - PreserveNewest - - - - diff --git a/bindings/dotnet/samples/04-HelloInputs/Program.cs b/bindings/dotnet/samples/04-HelloInputs/Program.cs deleted file mode 100644 index 45eb2ce9..00000000 --- a/bindings/dotnet/samples/04-HelloInputs/Program.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System.Diagnostics; -using System.Numerics; -using Elemental; -using Elemental.Graphics; -using Elemental.Inputs; -using Elemental.Tools; - -static void LogMessageHandler(LogMessageType messageType, LogMessageCategory category, string function, string message) -{ - var mainForegroundColor = messageType switch - { - LogMessageType.Warning => ConsoleColor.Yellow, - LogMessageType.Error => ConsoleColor.Red, - _ => ConsoleColor.Gray - }; - - Console.Write("["); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write($"{category}"); - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write("]"); - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write($" {function}"); - - Console.ForegroundColor = mainForegroundColor; - Console.WriteLine($" {message}"); - Console.ForegroundColor = ConsoleColor.Gray; -} - -using var applicationService = new NativeApplicationService(new() { LogMessageHandler = LogMessageHandler }); -using var graphicsService = new GraphicsService(new() { GraphicsDiagnostics = GraphicsDiagnostics.Debug }); -using var inputsService = new InputsService(); -using var inputsQueue = inputsService.CreateInputsQueue(); - -using var application = applicationService.CreateApplication("Hello Inputs"); -using var window = applicationService.CreateWindow(application); - -var availableGraphicsDevices = graphicsService.GetAvailableGraphicsDevices(); -var selectedGraphicsDevice = availableGraphicsDevices[0]; - -// TODO: Do a separate sample to showcase the use of Native + Vulkan with 2 windows (if available) -foreach (var availableGraphicsDevice in availableGraphicsDevices) -{ - if (availableGraphicsDevice.GraphicsApi == GraphicsApi.Vulkan) - { - //selectedGraphicsDevice = availableGraphicsDevice; - } - - Console.WriteLine($"{availableGraphicsDevice}"); -} - -using var graphicsDevice = graphicsService.CreateGraphicsDevice(new() { DeviceId = selectedGraphicsDevice.DeviceId }); -using var commandQueue = graphicsService.CreateCommandQueue(graphicsDevice, CommandQueueType.Graphics); - -applicationService.SetWindowTitle(window, $"Hello Inputs! (GraphicsDevice: {graphicsService.GetGraphicsDeviceInfo(graphicsDevice)})"); - -using var swapChain = graphicsService.CreateSwapChain(window, commandQueue, new() { MaximumFrameLatency = 1 }); -var currentRenderSize = applicationService.GetWindowRenderSize(window); - -// HACK: This is needed when we run the program from the root directory -Environment.CurrentDirectory = Path.GetDirectoryName(Environment.ProcessPath)!; - -using var shaderCompiler = new ShaderCompiler(new() { LogMessageHandler = LogMessageHandler }); - -var shaderCode = File.ReadAllText("Triangle.hlsl"); - -// TODO: This is needed for Apple Metal because SPIRV-Cross doesn't yet support metal mesh shaders -var shaderCodeMetal = File.ReadAllText("Triangle.metal"); - -var meshShaderSourceType = selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal ? ShaderLanguage.Msl : ShaderLanguage.Hlsl; - -ShaderCompilerInput[] shaderInputs = -[ - new() { ShaderCode = selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal ? shaderCodeMetal : shaderCode, Stage = ShaderStage.MeshShader, EntryPoint = "MeshMain", ShaderLanguage = meshShaderSourceType }, - new() { ShaderCode = shaderCode, Stage = ShaderStage.PixelShader, EntryPoint = "PixelMain", ShaderLanguage = ShaderLanguage.Hlsl } -]; - -var shaderCompilationResults = shaderCompiler.CompileShaders(shaderInputs, selectedGraphicsDevice.GraphicsApi); - -// TODO: Avoid the array declaration -var shaderParts = new ShaderPart[shaderCompilationResults.Length]; - -for (var i = 0; i < shaderCompilationResults.Length; i++) -{ - var shaderCompilationResult = shaderCompilationResults[i]; - - foreach (var logEntry in shaderCompilationResult.LogEntries.Span) - { - Console.WriteLine($"{logEntry.Type}: {logEntry.Message}"); - } - - if (!shaderCompilationResult.IsSuccess) - { - return; - } - - shaderParts[i] = new() - { - Stage = shaderCompilationResult.Stage, - EntryPoint = shaderCompilationResult.EntryPoint, - Data = shaderCompilationResult.ShaderData, - MetaData = !(selectedGraphicsDevice.GraphicsApi == GraphicsApi.Metal && shaderCompilationResult.Stage == ShaderStage.MeshShader) ? shaderCompilationResult.MetaData : new ShaderMetaData[] - { - new ShaderMetaData { Type = ShaderMetaDataType.PushConstantsCount, Value = 1 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountX, Value = 32 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountY, Value = 1 }, - new ShaderMetaData { Type = ShaderMetaDataType.ThreadCountZ, Value = 1 } - } - }; -} - -using var shader = graphicsService.CreateShader(graphicsDevice, shaderParts); - -var frameTimer = new Stopwatch(); -var currentFrame = 0u; - -var shaderParameters = new ShaderParameters() -{ - AspectRatio = (float)currentRenderSize.Width / currentRenderSize.Height -}; - -applicationService.RunApplication(application, (status) => -{ - if (status.IsClosing) - { - return false; - } - - var renderSize = applicationService.GetWindowRenderSize(window); - - if (renderSize != currentRenderSize) - { - Console.WriteLine($"Resize SwapChain to: {renderSize}"); - graphicsService.ResizeSwapChain(swapChain, renderSize.Width, renderSize.Height); - currentRenderSize = renderSize; - - shaderParameters = shaderParameters with - { - AspectRatio = (float)currentRenderSize.Width / currentRenderSize.Height - }; - } - - graphicsService.WaitForSwapChainOnCpu(swapChain); - var inputState = inputsService.GetInputState(application); - - var inputsValues = inputsService.ReadInputsQueue(inputsQueue); - - if (inputsValues.Length > 0) - { - Console.WriteLine($"===== Inputs Queue {currentFrame} ====="); - - foreach (var inputsValue in inputsValues) - { - Console.WriteLine($"InputsValue: {inputsValue.Id}"); - } - - Console.WriteLine("===== End Inputs Queue ====="); - } - - shaderParameters = shaderParameters with - { - RotationX = shaderParameters.RotationX + -inputState.Gamepad.LeftStickY.Value * 5.0f * (float)frameTimer.Elapsed.TotalSeconds, - RotationY = shaderParameters.RotationY + inputState.Gamepad.LeftStickX.Value * 5.0f * (float)frameTimer.Elapsed.TotalSeconds, - RotationZ = shaderParameters.RotationZ + (inputState.Gamepad.RightShoulder.Value - inputState.Gamepad.LeftShoulder.Value) * 5.0f * (float)frameTimer.Elapsed.TotalSeconds, - TranslationX = shaderParameters.TranslationX + (inputState.Gamepad.RightStickX.Value + (inputState.Gamepad.DpadRight.Value - inputState.Gamepad.DpadLeft.Value)) * 5.0f * (float)frameTimer.Elapsed.TotalSeconds, - TranslationY = shaderParameters.TranslationY + (inputState.Gamepad.RightStickY.Value + (inputState.Gamepad.DpadUp.Value - inputState.Gamepad.DpadDown.Value)) * 5.0f * (float)frameTimer.Elapsed.TotalSeconds, - CurrentColorIndex = inputState.Gamepad.Button2.Value == 1 ? 1u : 0u - }; - - //Console.WriteLine($"Left: {inputState.Gamepad.DpadDown.Value}"); - - frameTimer.Restart(); - - using var commandList = graphicsService.CreateCommandList(commandQueue); - - using var backbufferTexture = graphicsService.GetSwapChainBackBufferTexture(swapChain); - graphicsService.BeginRenderPass(commandList, new() { RenderTarget0 = new() { Texture = backbufferTexture, ClearColor = new Vector4(0.0f, 0.0f, 0.0f, 1.0f) } }); - - graphicsService.SetShader(commandList, shader); - graphicsService.SetShaderConstants(commandList, 0, ref shaderParameters); - graphicsService.DispatchMesh(commandList, 1, 1, 1); - - graphicsService.EndRenderPass(commandList); - graphicsService.CommitCommandList(commandList); - graphicsService.ExecuteCommandList(commandQueue, commandList); - - graphicsService.PresentSwapChain(swapChain); - currentFrame++; - return true; -}); - -readonly record struct ShaderParameters -{ - public float RotationX { get; init; } - public float RotationY { get; init; } - public float RotationZ { get; init; } - public float TranslationX { get; init; } - public float TranslationY { get; init; } - public float TranslationZ { get; init; } - public float AspectRatio { get; init; } - public uint CurrentColorIndex { get; init; } -} diff --git a/bindings/dotnet/samples/04-HelloInputs/Triangle.hlsl b/bindings/dotnet/samples/04-HelloInputs/Triangle.hlsl deleted file mode 100644 index be5bc388..00000000 --- a/bindings/dotnet/samples/04-HelloInputs/Triangle.hlsl +++ /dev/null @@ -1,128 +0,0 @@ -#define RootSignatureDef "RootFlags(0), RootConstants(num32BitConstants=8, b0)" - -struct ShaderParameters -{ - float RotationX; - float RotationY; - float RotationZ; - float TranslationX; - float TranslationY; - float TranslationZ; - float AspectRatio; - uint CurrentColorIndex; -}; - -[[vk::push_constant]] -ShaderParameters parameters : register(b0); - -struct Vertex -{ - float3 Position; - float4 Color; -}; - -struct VertexOutput -{ - float4 Position: SV_Position; - float4 Color: TEXCOORD0; -}; - -static Vertex triangleVertices[] = -{ - { float3(-0.5, 0.5, 0.0), float4(1.0, 0.0, 0.0, 1.0) }, - { float3(0.5, 0.5, 0.0), float4(0.0, 1.0, 0.0, 1.0) }, - { float3(-0.5, -0.5, 0.0), float4(0.0, 0.0, 1.0, 1.0) } -}; - -static float4 Colors[] = -{ - float4(1.0, 0.0, 0.0, 1.0), float4(0.0, 1.0, 0.0, 1.0), float4(0.0, 0.0, 1.0, 1.0), - float4(1.0, 1.0, 0.0, 1.0), float4(0.0, 1.0, 1.0, 1.0), float4(1.0, 0.0, 1.0, 1.0) -}; - -float4x4 LookAtLHMatrix(float3 eyePosition, float3 targetPosition, float3 upDirection) -{ - float3 forwardDirection = normalize(targetPosition - eyePosition); - float3 rightDirection = normalize(cross(upDirection, forwardDirection)); - float3 upDirectionNew = cross(forwardDirection, rightDirection); - - float4 row1 = float4(rightDirection.x, upDirectionNew.x, forwardDirection.x, 0.0f); - float4 row2 = float4(rightDirection.y, upDirectionNew.y, forwardDirection.y, 0.0f); - float4 row3 = float4(rightDirection.z, upDirectionNew.z, forwardDirection.z, 0.0f); - float4 row4 = float4(-dot(rightDirection, eyePosition), -dot(upDirectionNew, eyePosition), -dot(forwardDirection, eyePosition), 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) -{ - float height = 1.0 / tan(fovY * 0.5); - - float4 row1 = float4(height / aspectRatio, 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, height, 0.0f, 0.0f); - float4 row3 = float4(0.0f, 0.0f, 0, 1.0f); - float4 row4 = float4(0.0f, 0.0f, zNear, 0.0f); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 RotationMatrix(float rotationX, float rotationY, float rotationZ) -{ - float cosX = cos(rotationX); - float sinX = sin(rotationX); - float cosY = cos(rotationY); - float sinY = sin(rotationY); - float cosZ = cos(rotationZ); - float sinZ = sin(rotationZ); - - float4 row1 = float4(cosY * cosZ, sinX * sinY * cosZ - cosX * sinZ, cosX * sinY * cosZ + sinX * sinZ, 0.0f); - float4 row2 = float4(cosY * sinZ, sinX * sinY * sinZ + cosX * cosZ, cosX * sinY * sinZ - sinX * cosZ, 0.0f); - float4 row3 = float4(-sinY, sinX * cosY, cosX * cosY, 0.0f); - float4 row4 = float4(0.0f, 0.0f, 0.0f, 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 TranslationMatrix(float translationX, float translationY, float translationZ) -{ - float4 row1 = float4(1.0f, 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, 1.0f, 0.0f, 0.0f); - float4 row3 = float4(0.0f, 0.0f, 1.0f, 0.0f); - float4 row4 = float4(translationX, translationY, translationZ, 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -[OutputTopology("triangle")] -[NumThreads(32, 1, 1)] -void MeshMain(in uint groupThreadId : SV_GroupThreadID, out vertices VertexOutput vertices[3], out indices uint3 indices[1]) -{ - const uint meshVertexCount = 3; - - SetMeshOutputCounts(meshVertexCount, 1); - - if (groupThreadId < meshVertexCount) - { - float cameraZDistance = parameters.AspectRatio >= 0.75 ? -2.0 : -4.0; - - float4x4 worldMatrix = RotationMatrix(0.0, parameters.RotationY, 0.0); - float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, cameraZDistance), float3(0, 0, 0), float3(0, 1, 0)); - float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); - - float4x4 worldViewProjectionMatrix = mul(worldMatrix, mul(viewMatrix, projectionMatrix)); - - - vertices[groupThreadId].Position = mul(float4(triangleVertices[groupThreadId].Position, 1), worldViewProjectionMatrix); - vertices[groupThreadId].Color = Colors[(parameters.CurrentColorIndex % 2) * 3 + groupThreadId]; - } - - if (groupThreadId == 0) - { - indices[groupThreadId] = uint3(0, 1, 2); - } -} - -float4 PixelMain(const VertexOutput input) : SV_Target0 -{ - return input.Color; -} diff --git a/bindings/dotnet/samples/04-HelloInputs/Triangle.metal b/bindings/dotnet/samples/04-HelloInputs/Triangle.metal deleted file mode 100644 index 29eabb1f..00000000 --- a/bindings/dotnet/samples/04-HelloInputs/Triangle.metal +++ /dev/null @@ -1,132 +0,0 @@ -using namespace metal; - -#include -#include - -struct VertexOutput -{ - float4 Position [[position]]; - float4 Color [[user(locn0)]]; -}; - -struct PrimitiveOutput -{ -}; - -struct ShaderParameters -{ - float RotationX; - float RotationY; - float RotationZ; - float TranslationX; - float TranslationY; - float TranslationZ; - float AspectRatio; - uint CurrentColorIndex; -}; - -constant float3 triangleVertices[] = -{ - float3(-0.5, 0.5, 0), - float3(0.5, 0.5, 0), - float3(-0.5, -0.5, 0) -}; - -constant float4 triangleColors[] = -{ - float4(1.0, 0.0, 0, 1), - float4(0.0, 1.0, 0, 1), - float4(0.0, 0.0, 1, 1) -}; - -constant uint8_t rectangleIndices[] = -{ - 0, 1, 2 -}; - -float4x4 RotationMatrix(float rotationX, float rotationY, float rotationZ) -{ - float cosX = cos(rotationX); - float sinX = sin(rotationX); - float cosY = cos(rotationY); - float sinY = sin(rotationY); - float cosZ = cos(rotationZ); - float sinZ = sin(rotationZ); - - float4 row1 = float4(cosY * cosZ, sinX * sinY * cosZ - cosX * sinZ, cosX * sinY * cosZ + sinX * sinZ, 0.0f); - float4 row2 = float4(cosY * sinZ, sinX * sinY * sinZ + cosX * cosZ, cosX * sinY * sinZ - sinX * cosZ, 0.0f); - float4 row3 = float4(-sinY, sinX * cosY, cosX * cosY, 0.0f); - float4 row4 = float4(0.0f, 0.0f, 0.0f, 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 LookAtLHMatrix(float3 eyePosition, float3 targetPosition, float3 upDirection) -{ - float3 forwardDirection = normalize(targetPosition - eyePosition); - float3 rightDirection = normalize(cross(upDirection, forwardDirection)); - float3 upDirectionNew = cross(forwardDirection, rightDirection); - - float4 row1 = float4(rightDirection.x, upDirectionNew.x, forwardDirection.x, 0.0f); - float4 row2 = float4(rightDirection.y, upDirectionNew.y, forwardDirection.y, 0.0f); - float4 row3 = float4(rightDirection.z, upDirectionNew.z, forwardDirection.z, 0.0f); - float4 row4 = float4(-dot(rightDirection, eyePosition), -dot(upDirectionNew, eyePosition), -dot(forwardDirection, eyePosition), 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -float4x4 PerspectiveProjectionMatrix(float fovY, float aspectRatio, float zNear) -{ - float height = 1.0 / tan(fovY * 0.5); - - float4 row1 = float4(height / aspectRatio, 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, height, 0.0f, 0.0f); - float4 row3 = float4(0.0f, 0.0f, 0, 1.0f); - float4 row4 = float4(0.0f, 0.0f, zNear, 0.0f); - - return float4x4(row1, row2, row3, row4); -} - -constant uint32_t meshVertexCount = 3; -constant uint32_t meshPrimitiveCount = 1; - -using AAPLTriangleMeshType = metal::mesh; - -/// The mesh stage function that generates a triangle mesh. -[[mesh, max_total_threads_per_threadgroup(126)]] -void MeshMain(AAPLTriangleMeshType output, - uint32_t groupThreadId [[thread_index_in_threadgroup]], - uint32_t groupId [[threadgroup_position_in_grid]], - device const ShaderParameters ¶meters [[buffer(0)]]) -{ - - - output.set_primitive_count(meshPrimitiveCount); - - if (groupThreadId < meshVertexCount) - { - VertexOutput vertexOutput = {}; - - float4x4 worldMatrix = RotationMatrix(parameters.RotationX, parameters.RotationY, parameters.RotationZ); - float4x4 viewMatrix = LookAtLHMatrix(float3(0, 0, -2), float3(0, 0, 0), float3(0, 1, 0)); - float4x4 projectionMatrix = PerspectiveProjectionMatrix(0.78, parameters.AspectRatio, 0.001); - - float4x4 worldViewProjectionMatrix = projectionMatrix * viewMatrix * worldMatrix; - - vertexOutput.Position = worldViewProjectionMatrix * float4(triangleVertices[groupThreadId], 1); - vertexOutput.Color = triangleColors[groupThreadId]; - - output.set_vertex(groupThreadId, vertexOutput); - } - - if (groupThreadId < meshPrimitiveCount) - { - PrimitiveOutput primitiveOutput; - output.set_primitive(groupThreadId, primitiveOutput); - - uint i = (3*groupThreadId); - output.set_index(i+0, rectangleIndices[i+0]); - output.set_index(i+1, rectangleIndices[i+1]); - output.set_index(i+2, rectangleIndices[i+2]); - } -} diff --git a/bindings/dotnet/samples/xx-MultiThread/HelloTriangle.csproj b/bindings/dotnet/samples/xx-MultiThread/HelloTriangle.csproj deleted file mode 100644 index 5c62edbe..00000000 --- a/bindings/dotnet/samples/xx-MultiThread/HelloTriangle.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - True - - - - $(AssemblyName).app/Contents/MacOS - - - - - - - diff --git a/bindings/dotnet/samples/xx-MultiThread/Program.cs b/bindings/dotnet/samples/xx-MultiThread/Program.cs deleted file mode 100644 index 4062b7c4..00000000 --- a/bindings/dotnet/samples/xx-MultiThread/Program.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Numerics; -using Elemental; -using Elemental.Graphics; - -var applicationService = new NativeApplicationService(); -using var application = applicationService.CreateApplication("Hello Window"); - -using var window = applicationService.CreateWindow(application, new NativeWindowOptions -{ - Title = "Hello Window!", - Width = 1280, - Height = 720, - WindowState = NativeWindowState.Normal -}); - -using var graphicsService = new GraphicsService(new GraphicsServiceOptions -{ - GraphicsDiagnostics = GraphicsDiagnostics.Debug -}); - -var availableGraphicsDevices = graphicsService.GetAvailableGraphicsDevices(); -var selectedGraphicsDevice = availableGraphicsDevices[0]; - -foreach (var availableGraphicsDevice in availableGraphicsDevices) -{ - if (availableGraphicsDevice.GraphicsApi == GraphicsApi.Vulkan) - { - selectedGraphicsDevice = availableGraphicsDevice; - } - - Console.WriteLine($"{availableGraphicsDevice}"); -} - -using var graphicsDevice = graphicsService.CreateGraphicsDevice(new GraphicsDeviceOptions -{ - //DeviceId = selectedGraphicsDevice.DeviceId -}); - -using var commandQueue = graphicsService.CreateCommandQueue(graphicsDevice, CommandQueueType.Graphics); -graphicsService.SetCommandQueueLabel(commandQueue, "Test Render Queue"); - -var graphicsDeviceInfos = graphicsService.GetGraphicsDeviceInfo(graphicsDevice); -applicationService.SetWindowTitle(window, $"Hello Triangle! (GraphicsDevice: {graphicsDeviceInfos})"); - -using var swapChain = graphicsService.CreateSwapChain(window, commandQueue, new SwapChainOptions -{ - Format = SwapChainFormat.Default -}); - -var currentRenderSize = applicationService.GetWindowRenderSize(window); - -var meshShaderData = new byte[10]; -var pixelShaderData = new byte[10]; - -/* -var shaderParts = stackalloc ShaderPart[2]; - -shaderParts[0] = new ShaderPart { Stage = ShaderStage.MeshShader, Data = meshShaderData }; -shaderParts[1] = new ShaderPart { Stage = ShaderStage.PixelShader, Data = pixelShaderData }; - -using var shader = graphicsService.CreateShader();*/ - -applicationService.RunApplication(application, (status) => -{ - if (status.IsClosing) - { - Console.WriteLine("Closing Application..."); - return false; - } - - var renderSize = applicationService.GetWindowRenderSize(window); - - if (renderSize != currentRenderSize) - { - Console.WriteLine($"Resize SwapChain to: {renderSize}"); - graphicsService.ResizeSwapChain(swapChain, renderSize.Width, renderSize.Height); - currentRenderSize = renderSize; - } - - graphicsService.WaitForSwapChainOnCpu(swapChain); - - var threadCount = 5; - var commandListCount = 5; - - var commandLists = new CommandList[threadCount * commandListCount]; - - using var backbufferTexture = graphicsService.GetSwapChainBackBufferTexture(swapChain); - - Parallel.For (0, threadCount, (i) => - //for (int i = 0; i < threadCount; i++) - { - for (var j = 0; j < commandListCount; j++) - { - var commandList = graphicsService.CreateCommandList(commandQueue); - graphicsService.SetCommandListLabel(commandList, $"Triangle CommandList {j} (Thread :{i})"); - - graphicsService.BeginRenderPass(commandList, new RenderPassDescriptor - { - RenderTarget0 = new RenderPassRenderTarget - { - Texture = backbufferTexture, - ClearColor = new Vector4(0.8f, 0.5f, 0.0f, 1.0f) - } - }); - - // TODO: Add a counter in the mesh dispatch push constant to test the frame latency - - graphicsService.EndRenderPass(commandList); - - graphicsService.BeginRenderPass(commandList, new RenderPassDescriptor - { - RenderTarget0 = new RenderPassRenderTarget - { - Texture = backbufferTexture, - ClearColor = new Vector4(0.0f, 1.0f, i == (threadCount - 1) && j == (commandListCount - 1) ? 1.0f : 0.0f, 1.0f) - } - }); - - graphicsService.EndRenderPass(commandList); - - graphicsService.CommitCommandList(commandList); - - commandLists[i * commandListCount + j] = commandList; - } - //} - }); - - graphicsService.ExecuteCommandLists(commandQueue, commandLists, Array.Empty()); - - for (var i = 0; i < commandLists.Length; i++) - { - commandLists[i].Dispose(); - } - - graphicsService.PresentSwapChain(swapChain); - - return true; -}); \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/Elemental.Tools.csproj b/bindings/dotnet/src/Elemental.Tools/Elemental.Tools.csproj deleted file mode 100644 index 7076d302..00000000 --- a/bindings/dotnet/src/Elemental.Tools/Elemental.Tools.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - true - true - true - true - - - - - - - - - - - - - - - Elemental.Tools.Native.dll - PreserveNewest - - - Elemental.Tools.Native.pdb - PreserveNewest - - - dxcompiler.dll - PreserveNewest - - - dxil.dll - PreserveNewest - - - - - - Elemental.Tools.Native.dylib - PreserveNewest - - - libdxcompiler.dylib - PreserveNewest - - - - diff --git a/bindings/dotnet/src/Elemental.Tools/Globals.cs b/bindings/dotnet/src/Elemental.Tools/Globals.cs deleted file mode 100644 index 928ca6cb..00000000 --- a/bindings/dotnet/src/Elemental.Tools/Globals.cs +++ /dev/null @@ -1,6 +0,0 @@ -global using System.Runtime.InteropServices; -global using System.Runtime.InteropServices.Marshalling; - -global using Elemental.Graphics; - -[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/IShaderCompiler.cs b/bindings/dotnet/src/Elemental.Tools/IShaderCompiler.cs deleted file mode 100644 index 7c4a23f7..00000000 --- a/bindings/dotnet/src/Elemental.Tools/IShaderCompiler.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Represents a shader compiler that can be used to compile shaders for a specific graphics API. -/// -[PlatformService(InitMethod = nameof(PlatformServiceInterop.Native_InitShaderCompiler), DisposeMethod = nameof(PlatformServiceInterop.Native_FreeShaderCompiler))] -public interface IShaderCompiler -{ - /// - /// Determines whether the shader compiler can compile shaders for the specified shader language and graphics API. - /// - /// The shader language to check. - /// The graphics API to check. - /// true if the shader compiler can compile shaders for the specified shader language and graphics API; otherwise, false. - bool CanCompileShader(ShaderLanguage shaderLanguage, GraphicsApi graphicsApi); - - /// - /// Compiles the specified shader code into a shader object. - /// - /// The source code of the shader to compile. - /// The stage of the shader to compile. - /// The name of the entry point function for the shader. - /// The language of the shader to compile. - /// The graphics API to compile the shader for. - /// The compilation options to use for the shader. - /// A object representing the compiled shader. - ShaderCompilerResult CompileShader(string shaderCode, ShaderStage shaderStage, string entryPoint, ShaderLanguage shaderLanguage, GraphicsApi graphicsApi, in ShaderCompilationOptions options = default); - - /// - /// Compiles an array of shader code into shader objects. - /// - /// An array of shader compiler inputs. - /// The graphics API to compile the shaders for. - /// The compilation options to use for the shaders. - /// An array of objects representing the compiled shaders. - ReadOnlySpan CompileShaders(ReadOnlySpan inputs, GraphicsApi graphicsApi, in ShaderCompilationOptions options = default); -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/PlatformServiceInterop.cs b/bindings/dotnet/src/Elemental.Tools/PlatformServiceInterop.cs deleted file mode 100644 index 9bd8acb9..00000000 --- a/bindings/dotnet/src/Elemental.Tools/PlatformServiceInterop.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Elemental.Tools; -[assembly:DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] - -namespace Elemental; - -internal static partial class PlatformServiceInterop -{ - [LibraryImport("Elemental.Tools.Native")] - internal static partial void Native_FreeNativePointer(nint pointer); - - [LibraryImport("Elemental.Tools.Native")] - internal static partial void Native_InitShaderCompiler(in ShaderCompilerOptions options = default); - - [LibraryImport("Elemental.Tools.Native")] - internal static partial void Native_FreeShaderCompiler(); - - [LibraryImport("Elemental.Tools.Native")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool Native_CanCompileShader(ShaderLanguage shaderLanguage, GraphicsApi graphicsApi); - - [LibraryImport("Elemental.Tools.Native", StringMarshalling = StringMarshalling.Utf8)] - internal static partial ShaderCompilerResult Native_CompileShader(string shaderCode, ShaderStage shaderStage, string entryPoint, ShaderLanguage shaderLanguage, GraphicsApi graphicsApi, in ShaderCompilationOptions options); - - [LibraryImport("Elemental.Tools.Native")] - internal unsafe static partial void Native_CompileShaders(ReadOnlySpan inputs, int inputCount, GraphicsApi graphicsApi, in ShaderCompilationOptions options, ShaderCompilerResultMarshaller.ShaderCompilerResultUnmanaged* results, out int resultCount); -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilationOptions.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilationOptions.cs deleted file mode 100644 index 7b3014e8..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilationOptions.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Specifies options for compiling shaders. -/// -public readonly record struct ShaderCompilationOptions -{ - /// - /// Initializes a new instance of the struct with the default options. - /// - public ShaderCompilationOptions() - { - DebugMode = false; - } - - /// - /// Gets or sets a value indicating whether the shader should be compiled in debug mode. - /// - public bool DebugMode { get; init; } - - // TODO: Add specialization constants -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerInput.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilerInput.cs deleted file mode 100644 index 0bb906e8..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerInput.cs +++ /dev/null @@ -1,61 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Represents a shader compiler input that includes shader code, shader stage, and entry point. -/// -[NativeMarshalling(typeof(ShaderCompilerInputMarshaller))] -public readonly record struct ShaderCompilerInput -{ - /// - /// Gets or sets the source code of the shader to compile. - /// - public required string ShaderCode { get; init; } - - /// - /// Gets or sets the source code of the shader to compile. - /// - public required ShaderStage Stage { get; init; } - - /// - /// Gets or sets the name of the entry point function for the shader. - /// - public required string EntryPoint { get; init; } - - /// - /// Gets or sets the language of the shaders to compile. - /// - public required ShaderLanguage ShaderLanguage { get; init; } -} - -[CustomMarshaller(typeof(ShaderCompilerInput), MarshalMode.Default, typeof(ShaderCompilerInputMarshaller))] -internal static unsafe class ShaderCompilerInputMarshaller -{ - internal readonly struct ShaderCompilerInputUnmanaged - { - public byte* ShaderCodePointer { get; init; } - public ShaderStage Stage { get; init; } - public byte* EntryPoint { get; init; } - public ShaderLanguage ShaderLanguage { get; init; } - } - - public static ShaderCompilerInputUnmanaged ConvertToUnmanaged(ShaderCompilerInput managed) - { - return new ShaderCompilerInputUnmanaged() - { - ShaderCodePointer = Utf8StringMarshaller.ConvertToUnmanaged(managed.ShaderCode), - Stage = managed.Stage, - EntryPoint = Utf8StringMarshaller.ConvertToUnmanaged(managed.EntryPoint), - ShaderLanguage = managed.ShaderLanguage - }; - } - - public static ShaderCompilerInput ConvertToManaged(ShaderCompilerInputUnmanaged unmanaged) - { - throw new NotImplementedException(); - } - - public static void Free(ShaderCompilerInputUnmanaged unmanaged) - { - Utf8StringMarshaller.Free(unmanaged.ShaderCodePointer); - } -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntry.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntry.cs deleted file mode 100644 index 45921574..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntry.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Structure that represents a shader compiler log entry. -/// -public readonly record struct ShaderCompilerLogEntry -{ - /// - /// Gets the type of the log entry. - /// - /// Type of the log entry. - public required ShaderCompilerLogEntryType Type { get; init; } - - /// - /// Gets the message of the log entry. - /// - /// Message of the log entry. - public required string Message { get; init; } -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntryType.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntryType.cs deleted file mode 100644 index 5c8d0513..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerLogEntryType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Enumerates the type of . -/// -public enum ShaderCompilerLogEntryType -{ - /// - /// Message log type. - /// - Message = 0, - - /// - /// Warning log type. - /// - Warning = 1, - - /// - /// Error log type. - /// - Error = 2 -} diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerOptions.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilerOptions.cs deleted file mode 100644 index 120ac676..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerOptions.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Describes a to create. -/// -[NativeMarshalling(typeof(ShaderCompilerOptionsMarshaller))] -public readonly record struct ShaderCompilerOptions -{ - /// - /// Default constructor. - /// - public ShaderCompilerOptions() - { - } - - /// - /// Gets or sets an handle that will receive debug messages. - /// - /// Log message handler. - public LogMessageHandler? LogMessageHandler { get; init; } -} - -[CustomMarshaller(typeof(ShaderCompilerOptions), MarshalMode.ManagedToUnmanagedIn, typeof(ShaderCompilerOptionsMarshaller))] -internal static unsafe class ShaderCompilerOptionsMarshaller -{ - internal struct ShaderCompilerOptionsUnmanaged - { - public nint LogMessageHandler; - } - - public static ShaderCompilerOptionsUnmanaged ConvertToUnmanaged(ShaderCompilerOptions managed) - { - return new ShaderCompilerOptionsUnmanaged - { - LogMessageHandler = managed.LogMessageHandler != null ? LogMessageHandlerMarshaller.ConvertToUnmanaged(managed.LogMessageHandler) : nint.Zero - }; - } - - public static void Free(ShaderCompilerOptionsUnmanaged _) - { - } -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerResult.cs b/bindings/dotnet/src/Elemental.Tools/ShaderCompilerResult.cs deleted file mode 100644 index 096e8036..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderCompilerResult.cs +++ /dev/null @@ -1,113 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Structure that represents a shader compiler result. -/// -[NativeMarshalling(typeof(ShaderCompilerResultMarshaller))] -public readonly record struct ShaderCompilerResult -{ - /// - /// Gets a value indicating if the compilation was successful. - /// - /// True if the compilation was successful; Otherwise, false. - public required bool IsSuccess { get; init; } - - /// - /// Gets the stage to which the shader compiler was compiled. - /// - /// Shader stage. - public required ShaderStage Stage { get; init; } - - /// - /// Gets the entry point that was used to compile the shader. - /// - /// Entry point name. - public required string EntryPoint { get; init; } - - /// - /// Gets the compiled shader data. - /// - /// Compiled shader data. - public ReadOnlyMemory ShaderData { get; init; } - - /// - /// Gets the log entries associated with the compilation. - /// - /// Log entries of the compilation. - public ReadOnlyMemory LogEntries { get; init; } - - /// - /// Gets the meta data values associated with the compilation. - /// - /// Meta data values. - public ReadOnlyMemory MetaData { get; init; } -} - -[CustomMarshaller(typeof(ShaderCompilerResult), MarshalMode.Default, typeof(ShaderCompilerResultMarshaller))] -internal static unsafe class ShaderCompilerResultMarshaller -{ - internal readonly struct ShaderCompilerLogEntryUnmanaged - { - public ShaderCompilerLogEntryType Type { get; init; } - public byte* Message { get; init; } - } - - internal readonly struct ShaderCompilerResultUnmanaged - { - public bool IsSuccess { get; init; } - public ShaderStage Stage { get; init; } - public byte* EntryPoint { get; init; } - public void* ShaderDataPointer { get; init; } - public uint ShaderDataCount { get; init; } - public ShaderCompilerLogEntryUnmanaged* LogEntryPointer { get; init; } - public uint LogEntryCount { get; init; } - public ShaderMetaData* MetaDataPointer { get; init; } - public uint MetaDataCount { get; init; } - } - - public static ShaderCompilerResultUnmanaged ConvertToUnmanaged(ShaderCompilerResult _) - { - throw new NotImplementedException(); - } - - public static ShaderCompilerResult ConvertToManaged(ShaderCompilerResultUnmanaged unmanaged) - { - // TODO: Avoid the string conversions - - var shaderData = unmanaged.ShaderDataPointer != null ? new byte[unmanaged.ShaderDataCount] : Array.Empty(); - var sourceShaderDataSpan = new Span(unmanaged.ShaderDataPointer, (int)unmanaged.ShaderDataCount); - sourceShaderDataSpan.CopyTo(shaderData); - - var logEntries = unmanaged.LogEntryPointer != null ? new ShaderCompilerLogEntry[unmanaged.LogEntryCount] : Array.Empty(); - var sourceLogEntriesSpan = new Span(unmanaged.LogEntryPointer, (int)unmanaged.LogEntryCount); - - var shaderMetaData = unmanaged.MetaDataPointer != null ? new ShaderMetaData[unmanaged.MetaDataCount] : Array.Empty(); - var sourceMetaDataSpan = new Span(unmanaged.MetaDataPointer, (int)unmanaged.MetaDataCount); - sourceMetaDataSpan.CopyTo(shaderMetaData); - - for (var i = 0; i < unmanaged.LogEntryCount; i++) - { - var sourceLogEntry = sourceLogEntriesSpan[i]; - - logEntries[i] = new ShaderCompilerLogEntry - { - Type = sourceLogEntry.Type, - Message = Utf8StringMarshaller.ConvertToManaged(sourceLogEntry.Message) ?? string.Empty - }; - } - - return new ShaderCompilerResult - { - IsSuccess = unmanaged.IsSuccess, - Stage = unmanaged.Stage, - EntryPoint = Utf8StringMarshaller.ConvertToManaged(unmanaged.EntryPoint) ?? string.Empty, - ShaderData = shaderData, - LogEntries = logEntries, - MetaData = shaderMetaData - }; - } - - public static void Free(ShaderCompilerResultUnmanaged _) - { - } -} diff --git a/bindings/dotnet/src/Elemental.Tools/ShaderLanguage.cs b/bindings/dotnet/src/Elemental.Tools/ShaderLanguage.cs deleted file mode 100644 index a5030fd7..00000000 --- a/bindings/dotnet/src/Elemental.Tools/ShaderLanguage.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Elemental.Tools; - -/// -/// Enumerates the supported shader languages of the shader compiler. -/// -public enum ShaderLanguage -{ - /// - /// Unknown Shader Language. - /// - Unknown = 0, - - /// - /// DirectX Shader Language. - /// - Hlsl = 1, - - /// - /// Metal Shader Language. - /// - Msl = 2, - - /// - /// DirectX shader bytecode. - /// - Dxil = 3, - - /// - /// Vulkan shader bytecode. - /// - Spirv = 4, - - /// - /// Metal shader bytecode. - /// - MetalIR = 5 -} \ No newline at end of file diff --git a/bindings/dotnet/src/Elemental/ApplicationHandler.cs b/bindings/dotnet/src/Elemental/ApplicationHandler.cs deleted file mode 100644 index d010b7c5..00000000 --- a/bindings/dotnet/src/Elemental/ApplicationHandler.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Elemental; - -/// -/// Defines a function pointer type for handling application events. -/// -public delegate void ApplicationHandler(ref T payload) where T : unmanaged; diff --git a/bindings/dotnet/src/Elemental/ApplicationService.cs b/bindings/dotnet/src/Elemental/ApplicationService.cs deleted file mode 100644 index cb55d807..00000000 --- a/bindings/dotnet/src/Elemental/ApplicationService.cs +++ /dev/null @@ -1,177 +0,0 @@ -namespace Elemental; - -internal static class InitApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - -internal static class FreeApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - -/// -public class ApplicationService : IApplicationService -{ - /// - /// Configures a custom log handler for processing log messages generated by the application. - /// - /// The function to call when a log message is generated. - public void ConfigureLogHandler(LogHandler logHandler) - { - ApplicationServiceInterop.ConfigureLogHandler(logHandler); - } - - /// - /// Retrieves system-related information, such as platform and application path. - /// - /// A structure containing system information. - public unsafe SystemInfo GetSystemInfo() - { - var resultUnsafe = ApplicationServiceInterop.GetSystemInfo(); - - var result = new SystemInfo(); - result.Platform = resultUnsafe.Platform; - - // TODO: New code to generate - result.ApplicationPath = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(resultUnsafe.ApplicationPath); - result.SupportMultiWindows = resultUnsafe.SupportMultiWindows; - - return result; - } - - /// - /// Starts the execution of an application with specified parameters. - /// - /// Configuration and handlers for the application lifecycle. - /// Status code indicating success or error. - public unsafe int RunApplication(in RunApplicationParameters parameters) where T : unmanaged - { - // TODO: New code to generate - fixed (byte* ApplicationNamePinned = parameters.ApplicationName) - { - var parametersUnsafe = new RunApplicationParametersUnsafe(); - parametersUnsafe.ApplicationName = ApplicationNamePinned; - - InitApplicationHandler._interceptorEntry = parameters.InitHandler; - parametersUnsafe.InitHandler = (nint)(delegate* )&InitApplicationHandler.Interceptor; - - FreeApplicationHandler._interceptorEntry = parameters.FreeHandler; - parametersUnsafe.FreeHandler = (nint)(delegate* )&FreeApplicationHandler.Interceptor; - - var test = stackalloc T[1]; - *test = parameters.Payload; - parametersUnsafe.Payload = (nint)test; - - return ApplicationServiceInterop.RunApplication(parametersUnsafe); - } - } - - /// - /// Exits the application, performing necessary cleanup. - /// - /// Exit code of the application. - public void ExitApplication(int exitCode) - { - ApplicationServiceInterop.ExitApplication(exitCode); - } - - /// - /// Creates a window with the specified options or default settings if none are provided. - /// - /// Configuration options for the window; NULL for defaults. - /// A handle to the newly created window. - public unsafe Window CreateWindow(in WindowOptions options = default) - { - fixed (byte* TitlePinned = options.Title) - { - var optionsUnsafe = new WindowOptionsUnsafe(); - optionsUnsafe.Title = TitlePinned; - optionsUnsafe.Width = options.Width; - optionsUnsafe.Height = options.Height; - optionsUnsafe.WindowState = options.WindowState; - optionsUnsafe.IsCursorHidden = options.IsCursorHidden; - - return ApplicationServiceInterop.CreateWindow(optionsUnsafe); - } - } - - /// - /// Releases resources associated with a window. - /// - /// Handle to the window to be freed. - public void FreeWindow(Window window) - { - ApplicationServiceInterop.FreeWindow(window); - } - - /// - /// Gets the render size of a window, accounting for DPI scaling. - /// - /// The window instance. - /// Render size of the window. - public WindowSize GetWindowRenderSize(Window window) - { - return ApplicationServiceInterop.GetWindowRenderSize(window); - } - - /// - /// Sets a window's title. - /// - /// The window instance. - /// New title for the window. - public void SetWindowTitle(Window window, ReadOnlySpan title) - { - ApplicationServiceInterop.SetWindowTitle(window, title); - } - - /// - /// Changes the state of a window (e.g., minimize, maximize). - /// - /// The window instance. - /// New state for the window. - public void SetWindowState(Window window, WindowState windowState) - { - ApplicationServiceInterop.SetWindowState(window, windowState); - } - - /// - /// TODO: Comments -///TODO: Make sure the coordinates are consistent accross all platforms - /// - public void ShowWindowCursor(Window window) - { - ApplicationServiceInterop.ShowWindowCursor(window); - } - - public void HideWindowCursor(Window window) - { - ApplicationServiceInterop.HideWindowCursor(window); - } - - public WindowCursorPosition GetWindowCursorPosition(Window window) - { - return ApplicationServiceInterop.GetWindowCursorPosition(window); - } - -} diff --git a/bindings/dotnet/src/Elemental/ApplicationServiceInterop.cs b/bindings/dotnet/src/Elemental/ApplicationServiceInterop.cs deleted file mode 100644 index 16755d7c..00000000 --- a/bindings/dotnet/src/Elemental/ApplicationServiceInterop.cs +++ /dev/null @@ -1,55 +0,0 @@ -[assembly:DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] - -namespace Elemental; - -internal static partial class ApplicationServiceInterop -{ - [LibraryImport("Elemental.Native", EntryPoint = "ElemConfigureLogHandler")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ConfigureLogHandler(LogHandler logHandler); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetSystemInfo")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial SystemInfoUnsafe GetSystemInfo(); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemRunApplication")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial int RunApplication(in RunApplicationParametersUnsafe parameters); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemExitApplication")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ExitApplication(int exitCode); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemCreateWindow")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial Window CreateWindow(in WindowOptionsUnsafe options); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemFreeWindow")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void FreeWindow(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetWindowRenderSize")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial WindowSize GetWindowRenderSize(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemSetWindowTitle")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void SetWindowTitle(Window window, ReadOnlySpan title); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemSetWindowState")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void SetWindowState(Window window, WindowState windowState); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemShowWindowCursor")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ShowWindowCursor(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemHideWindowCursor")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void HideWindowCursor(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetWindowCursorPosition")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial WindowCursorPosition GetWindowCursorPosition(Window window); - -} diff --git a/bindings/dotnet/src/Elemental/ConsoleLogHandler.cs b/bindings/dotnet/src/Elemental/ConsoleLogHandler.cs deleted file mode 100644 index 3f0ee50a..00000000 --- a/bindings/dotnet/src/Elemental/ConsoleLogHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Elemental; - -/// -/// Class that contains a default log handler implementation. -/// -public static class DefaultLogHandlers -{ - /// - /// Default console log handler. - /// - /// Message type - /// Category - /// Function - /// Message - public static void ConsoleLogHandler(LogMessageType messageType, LogMessageCategory category, ReadOnlySpan function, ReadOnlySpan message) - { - var mainForegroundColor = messageType switch - { - LogMessageType.Warning => ConsoleColor.Yellow, - LogMessageType.Error => ConsoleColor.Red, - _ => ConsoleColor.Gray - }; - - Console.Write("["); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write($"{category}"); - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write("]"); - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write($" {System.Text.UTF8Encoding.UTF8.GetString(function)}"); - - Console.ForegroundColor = mainForegroundColor; - Console.WriteLine($" {messageType}: {System.Text.UTF8Encoding.UTF8.GetString(message)}"); - Console.ForegroundColor = ConsoleColor.Gray; - } -} diff --git a/bindings/dotnet/src/Elemental/Elemental.csproj b/bindings/dotnet/src/Elemental/Elemental.csproj deleted file mode 100644 index e74bb91b..00000000 --- a/bindings/dotnet/src/Elemental/Elemental.csproj +++ /dev/null @@ -1,50 +0,0 @@ - - - - true - true - true - true - - - - - - - - - Elemental.Native.dll - PreserveNewest - - - Elemental.Native.pdb - PreserveNewest - - - D3D12Core.dll - PreserveNewest - - - D3D12SDKLayers.dll - PreserveNewest - - - - - - - - - - - - diff --git a/bindings/dotnet/src/Elemental/Globals.cs b/bindings/dotnet/src/Elemental/Globals.cs deleted file mode 100644 index 01008824..00000000 --- a/bindings/dotnet/src/Elemental/Globals.cs +++ /dev/null @@ -1,7 +0,0 @@ -global using System.Numerics; -global using System.Runtime.InteropServices; -global using System.Runtime.InteropServices.Marshalling; -global using System.Runtime.CompilerServices; -global using System.Text; - -[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] diff --git a/bindings/dotnet/src/Elemental/IApplicationService.cs b/bindings/dotnet/src/Elemental/IApplicationService.cs deleted file mode 100644 index d55305ef..00000000 --- a/bindings/dotnet/src/Elemental/IApplicationService.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Elemental; - -/// -/// Defines an interface for Application services. -/// -public interface IApplicationService -{ - /// - /// Configures a custom log handler for processing log messages generated by the application. - /// - /// The function to call when a log message is generated. - void ConfigureLogHandler(LogHandler logHandler); - - /// - /// Retrieves system-related information, such as platform and application path. - /// - /// A structure containing system information. - SystemInfo GetSystemInfo(); - - /// - /// Starts the execution of an application with specified parameters. - /// - /// Configuration and handlers for the application lifecycle. - /// Status code indicating success or error. - int RunApplication(in RunApplicationParameters parameters) where T : unmanaged; - - /// - /// Exits the application, performing necessary cleanup. - /// - /// Exit code of the application. - void ExitApplication(int exitCode); - - /// - /// Creates a window with the specified options or default settings if none are provided. - /// - /// Configuration options for the window; NULL for defaults. - /// A handle to the newly created window. - Window CreateWindow(in WindowOptions options = default); - - /// - /// Releases resources associated with a window. - /// - /// Handle to the window to be freed. - void FreeWindow(Window window); - - /// - /// Gets the render size of a window, accounting for DPI scaling. - /// - /// The window instance. - /// Render size of the window. - WindowSize GetWindowRenderSize(Window window); - - /// - /// Sets a window's title. - /// - /// The window instance. - /// New title for the window. - void SetWindowTitle(Window window, ReadOnlySpan title); - - /// - /// Changes the state of a window (e.g., minimize, maximize). - /// - /// The window instance. - /// New state for the window. - void SetWindowState(Window window, WindowState windowState); - - /// - /// TODO: Comments -///TODO: Make sure the coordinates are consistent accross all platforms - /// - void ShowWindowCursor(Window window); - - void HideWindowCursor(Window window); - - WindowCursorPosition GetWindowCursorPosition(Window window); -} diff --git a/bindings/dotnet/src/Elemental/LogHandler.cs b/bindings/dotnet/src/Elemental/LogHandler.cs deleted file mode 100644 index 176a2329..00000000 --- a/bindings/dotnet/src/Elemental/LogHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Elemental; - -/// -/// Defines a function pointer type for log handling. -/// -/// The type of the log message. -/// The category of the log message. -/// The function where the log was triggered. -/// The log message. -[NativeMarshalling(typeof(LogHandlerMarshaller))] -public delegate void LogHandler(LogMessageType messageType, LogMessageCategory category, ReadOnlySpan function, ReadOnlySpan message); - -[CustomMarshaller(typeof(LogHandler), MarshalMode.ManagedToUnmanagedIn, typeof(LogHandlerMarshaller))] -internal static unsafe class LogHandlerMarshaller -{ - internal sealed unsafe record InterceptorEntry - { - public required LogHandler Callback { get; init; } - //public required GCHandle Handle { get; init; } - } - - private static InterceptorEntry? _interceptorEntry; - - private static unsafe void Interceptor(LogMessageType messageType, LogMessageCategory category, byte* function, byte* message) - { - if (_interceptorEntry == null || function == null || message == null) - { - return; - } - - var functionCounter = 0; - var functionPointer = (byte*)function; - - while (functionPointer[functionCounter] != 0) - { - functionCounter++; - } - - functionCounter++; - - var messageCounter = 0; - var messagePointer = (byte*)message; - - while (messagePointer[messageCounter] != 0) - { - messageCounter++; - } - - messageCounter++; - - _interceptorEntry.Callback(messageType, category, new ReadOnlySpan(function, functionCounter), new ReadOnlySpan(message, messageCounter)); - } - - public static nint ConvertToUnmanaged(LogHandler managed) - { - // TODO: Unallocate handle - //var interceptorDelegate = Interceptor; - //var handle = GCHandle.Alloc(interceptorDelegate); - //var unmanaged = Marshal.GetFunctionPointerForDelegate(interceptorDelegate); - - // TODO: Try to avoid all of that - delegate* unmanaged = &Interceptor; - - _interceptorEntry = new InterceptorEntry { Callback = managed }; - return (nint)unmanaged; - } - - public static void Free(nint _) - { - } -} diff --git a/bindings/dotnet/src/Elemental/LogMessageCategory.cs b/bindings/dotnet/src/Elemental/LogMessageCategory.cs deleted file mode 100644 index e49ad889..00000000 --- a/bindings/dotnet/src/Elemental/LogMessageCategory.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Elemental; - -/// -/// Categorizes log messages by their related system components. -/// -public enum LogMessageCategory -{ - /// - /// Assertions and checks. - /// - Assert = 0, - - /// - /// Memory allocation and management. - /// - Memory = 1, - - /// - /// General application behavior. - /// - Application = 2, - - /// - /// Graphics system-related messages. - /// - Graphics = 3, - - /// - /// Input system-related messages. - /// - Inputs = 4 -} diff --git a/bindings/dotnet/src/Elemental/LogMessageType.cs b/bindings/dotnet/src/Elemental/LogMessageType.cs deleted file mode 100644 index a8eb2eae..00000000 --- a/bindings/dotnet/src/Elemental/LogMessageType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Elemental; - -/// -/// Defines the types of log messages that can be generated. -/// -public enum LogMessageType -{ - /// - /// Debugging message. - /// - Debug = 0, - - /// - /// Warning message. - /// - Warning = 1, - - /// - /// Error message. - /// - Error = 2 -} diff --git a/bindings/dotnet/src/Elemental/Platform.cs b/bindings/dotnet/src/Elemental/Platform.cs deleted file mode 100644 index d578e044..00000000 --- a/bindings/dotnet/src/Elemental/Platform.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Lists the platforms supported by the library. -/// -public enum Platform -{ - /// - /// Microsoft Windows platform. - /// - Windows = 0, - - /// - /// Apple macOS platform. - /// - MacOS = 1, - - /// - /// Apple iOS platform. - /// - iOS = 2, - - /// - /// Linux platform. - /// - Linux = 3 -} diff --git a/bindings/dotnet/src/Elemental/RunApplicationParameters.cs b/bindings/dotnet/src/Elemental/RunApplicationParameters.cs deleted file mode 100644 index 7ef786d2..00000000 --- a/bindings/dotnet/src/Elemental/RunApplicationParameters.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Elemental; - -/// -/// Holds parameters for running an application, including initialization and cleanup routines. -/// -public ref struct RunApplicationParameters where T: unmanaged -{ - /// - /// Name of the application. - /// - public ReadOnlySpan ApplicationName { get; set; } - - /// - /// Function called at application startup. - /// - public ApplicationHandler InitHandler { get; set; } - - /// - /// Function called at application termination. - /// - public ApplicationHandler FreeHandler { get; set; } - - /// - /// Custom user data passed to handler functions. - /// - public T Payload { get; set; } -} - -internal unsafe struct RunApplicationParametersUnsafe -{ - public byte* ApplicationName { get; set; } - - public nint InitHandler { get; set; } - - public nint FreeHandler { get; set; } - - public nint Payload; -} - diff --git a/bindings/dotnet/src/Elemental/SpanUnsafe.cs b/bindings/dotnet/src/Elemental/SpanUnsafe.cs deleted file mode 100644 index e966f8ad..00000000 --- a/bindings/dotnet/src/Elemental/SpanUnsafe.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Elemental; - -internal unsafe record struct SpanUnsafe where T : struct -{ - internal nuint Items { get; set; } - internal int Length { get; set; } -} diff --git a/bindings/dotnet/src/Elemental/SystemInfo.cs b/bindings/dotnet/src/Elemental/SystemInfo.cs deleted file mode 100644 index f3ee7822..00000000 --- a/bindings/dotnet/src/Elemental/SystemInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Elemental; - -/// -/// Contains information about the system, useful for tailoring application behavior. -/// -public ref struct SystemInfo -{ - /// - /// Operating system platform. - /// - public Platform Platform { get; set; } - - /// - /// Installation path of the application. - /// - public ReadOnlySpan ApplicationPath { get; set; } - - /// - /// Whether the application supports multiple windows. - /// - public bool SupportMultiWindows { get; set; } -} - -internal unsafe struct SystemInfoUnsafe -{ - public Platform Platform { get; set; } - - public byte* ApplicationPath { get; set; } - - public bool SupportMultiWindows { get; set; } -} - diff --git a/bindings/dotnet/src/Elemental/Window.cs b/bindings/dotnet/src/Elemental/Window.cs deleted file mode 100644 index 6b616a12..00000000 --- a/bindings/dotnet/src/Elemental/Window.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Elemental; - -/// -/// Represents a handle to an elemental window. -/// -public readonly record struct Window : IDisposable -{ - private UInt64 Value { get; } - - /// - /// Disposes the handler. - /// - public void Dispose() - { - ApplicationServiceInterop.FreeWindow(this); - } -} diff --git a/bindings/dotnet/src/Elemental/WindowCursorPosition.cs b/bindings/dotnet/src/Elemental/WindowCursorPosition.cs deleted file mode 100644 index 6a67a13a..00000000 --- a/bindings/dotnet/src/Elemental/WindowCursorPosition.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Elemental; - -/// -/// TODO: Comments -/// -public ref struct WindowCursorPosition -{ - public uint X { get; set; } - - public uint Y { get; set; } -} diff --git a/bindings/dotnet/src/Elemental/WindowOptions.cs b/bindings/dotnet/src/Elemental/WindowOptions.cs deleted file mode 100644 index 06209487..00000000 --- a/bindings/dotnet/src/Elemental/WindowOptions.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Elemental; - -/// -/// Defines options for creating a new window. -/// -public ref struct WindowOptions -{ - /// - /// Title of the window. - /// - public ReadOnlySpan Title { get; set; } - - /// - /// Width of the window in pixels. - /// - public uint Width { get; set; } - - /// - /// Height of the window in pixels. - /// - public uint Height { get; set; } - - /// - /// Initial state of the window. - /// - public WindowState WindowState { get; set; } - - /// - /// True if the cursor should be hidden. - /// - public bool IsCursorHidden { get; set; } -} - -internal unsafe struct WindowOptionsUnsafe -{ - public byte* Title { get; set; } - - public uint Width { get; set; } - - public uint Height { get; set; } - - public WindowState WindowState { get; set; } - - public bool IsCursorHidden { get; set; } -} - diff --git a/bindings/dotnet/src/Elemental/WindowSize.cs b/bindings/dotnet/src/Elemental/WindowSize.cs deleted file mode 100644 index a9708ac8..00000000 --- a/bindings/dotnet/src/Elemental/WindowSize.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Contains detailed information about the size and scaling factors of a window's render area. -/// -public ref struct WindowSize -{ - /// - /// Width of the window's render area in pixels. - /// - public uint Width { get; set; } - - /// - /// Height of the window's render area in pixels. - /// - public uint Height { get; set; } - - /// - /// UI scale factor, typically used for DPI adjustments. - /// - public float UIScale { get; set; } - - /// - /// Current state of the window. - /// - public WindowState WindowState { get; set; } -} diff --git a/bindings/dotnet/src/Elemental/WindowState.cs b/bindings/dotnet/src/Elemental/WindowState.cs deleted file mode 100644 index 649a4749..00000000 --- a/bindings/dotnet/src/Elemental/WindowState.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Enumerates the possible states of a window. -/// -public enum WindowState -{ - /// - /// Window is in normal state. - /// - Normal = 0, - - /// - /// Window is minimized. - /// - Minimized = 1, - - /// - /// Window is maximized. - /// - Maximized = 2, - - /// - /// Window is in full screen mode. - /// - FullScreen = 3 -} diff --git a/bindings/dotnet/src/Elemental_REF/ApplicationHandler.cs b/bindings/dotnet/src/Elemental_REF/ApplicationHandler.cs deleted file mode 100644 index d010b7c5..00000000 --- a/bindings/dotnet/src/Elemental_REF/ApplicationHandler.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Elemental; - -/// -/// Defines a function pointer type for handling application events. -/// -public delegate void ApplicationHandler(ref T payload) where T : unmanaged; diff --git a/bindings/dotnet/src/Elemental_REF/ApplicationService.cs b/bindings/dotnet/src/Elemental_REF/ApplicationService.cs deleted file mode 100644 index cb55d807..00000000 --- a/bindings/dotnet/src/Elemental_REF/ApplicationService.cs +++ /dev/null @@ -1,177 +0,0 @@ -namespace Elemental; - -internal static class InitApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - -internal static class FreeApplicationHandler where T : unmanaged -{ - public static ApplicationHandler? _interceptorEntry; - - public static void Interceptor(ref T payload) - { - if (_interceptorEntry == null) - { - return; - } - - _interceptorEntry(ref payload); - } -} - -/// -public class ApplicationService : IApplicationService -{ - /// - /// Configures a custom log handler for processing log messages generated by the application. - /// - /// The function to call when a log message is generated. - public void ConfigureLogHandler(LogHandler logHandler) - { - ApplicationServiceInterop.ConfigureLogHandler(logHandler); - } - - /// - /// Retrieves system-related information, such as platform and application path. - /// - /// A structure containing system information. - public unsafe SystemInfo GetSystemInfo() - { - var resultUnsafe = ApplicationServiceInterop.GetSystemInfo(); - - var result = new SystemInfo(); - result.Platform = resultUnsafe.Platform; - - // TODO: New code to generate - result.ApplicationPath = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(resultUnsafe.ApplicationPath); - result.SupportMultiWindows = resultUnsafe.SupportMultiWindows; - - return result; - } - - /// - /// Starts the execution of an application with specified parameters. - /// - /// Configuration and handlers for the application lifecycle. - /// Status code indicating success or error. - public unsafe int RunApplication(in RunApplicationParameters parameters) where T : unmanaged - { - // TODO: New code to generate - fixed (byte* ApplicationNamePinned = parameters.ApplicationName) - { - var parametersUnsafe = new RunApplicationParametersUnsafe(); - parametersUnsafe.ApplicationName = ApplicationNamePinned; - - InitApplicationHandler._interceptorEntry = parameters.InitHandler; - parametersUnsafe.InitHandler = (nint)(delegate* )&InitApplicationHandler.Interceptor; - - FreeApplicationHandler._interceptorEntry = parameters.FreeHandler; - parametersUnsafe.FreeHandler = (nint)(delegate* )&FreeApplicationHandler.Interceptor; - - var test = stackalloc T[1]; - *test = parameters.Payload; - parametersUnsafe.Payload = (nint)test; - - return ApplicationServiceInterop.RunApplication(parametersUnsafe); - } - } - - /// - /// Exits the application, performing necessary cleanup. - /// - /// Exit code of the application. - public void ExitApplication(int exitCode) - { - ApplicationServiceInterop.ExitApplication(exitCode); - } - - /// - /// Creates a window with the specified options or default settings if none are provided. - /// - /// Configuration options for the window; NULL for defaults. - /// A handle to the newly created window. - public unsafe Window CreateWindow(in WindowOptions options = default) - { - fixed (byte* TitlePinned = options.Title) - { - var optionsUnsafe = new WindowOptionsUnsafe(); - optionsUnsafe.Title = TitlePinned; - optionsUnsafe.Width = options.Width; - optionsUnsafe.Height = options.Height; - optionsUnsafe.WindowState = options.WindowState; - optionsUnsafe.IsCursorHidden = options.IsCursorHidden; - - return ApplicationServiceInterop.CreateWindow(optionsUnsafe); - } - } - - /// - /// Releases resources associated with a window. - /// - /// Handle to the window to be freed. - public void FreeWindow(Window window) - { - ApplicationServiceInterop.FreeWindow(window); - } - - /// - /// Gets the render size of a window, accounting for DPI scaling. - /// - /// The window instance. - /// Render size of the window. - public WindowSize GetWindowRenderSize(Window window) - { - return ApplicationServiceInterop.GetWindowRenderSize(window); - } - - /// - /// Sets a window's title. - /// - /// The window instance. - /// New title for the window. - public void SetWindowTitle(Window window, ReadOnlySpan title) - { - ApplicationServiceInterop.SetWindowTitle(window, title); - } - - /// - /// Changes the state of a window (e.g., minimize, maximize). - /// - /// The window instance. - /// New state for the window. - public void SetWindowState(Window window, WindowState windowState) - { - ApplicationServiceInterop.SetWindowState(window, windowState); - } - - /// - /// TODO: Comments -///TODO: Make sure the coordinates are consistent accross all platforms - /// - public void ShowWindowCursor(Window window) - { - ApplicationServiceInterop.ShowWindowCursor(window); - } - - public void HideWindowCursor(Window window) - { - ApplicationServiceInterop.HideWindowCursor(window); - } - - public WindowCursorPosition GetWindowCursorPosition(Window window) - { - return ApplicationServiceInterop.GetWindowCursorPosition(window); - } - -} diff --git a/bindings/dotnet/src/Elemental_REF/ApplicationServiceInterop.cs b/bindings/dotnet/src/Elemental_REF/ApplicationServiceInterop.cs deleted file mode 100644 index 16755d7c..00000000 --- a/bindings/dotnet/src/Elemental_REF/ApplicationServiceInterop.cs +++ /dev/null @@ -1,55 +0,0 @@ -[assembly:DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)] - -namespace Elemental; - -internal static partial class ApplicationServiceInterop -{ - [LibraryImport("Elemental.Native", EntryPoint = "ElemConfigureLogHandler")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ConfigureLogHandler(LogHandler logHandler); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetSystemInfo")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial SystemInfoUnsafe GetSystemInfo(); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemRunApplication")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial int RunApplication(in RunApplicationParametersUnsafe parameters); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemExitApplication")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ExitApplication(int exitCode); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemCreateWindow")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial Window CreateWindow(in WindowOptionsUnsafe options); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemFreeWindow")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void FreeWindow(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetWindowRenderSize")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial WindowSize GetWindowRenderSize(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemSetWindowTitle")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void SetWindowTitle(Window window, ReadOnlySpan title); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemSetWindowState")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void SetWindowState(Window window, WindowState windowState); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemShowWindowCursor")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void ShowWindowCursor(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemHideWindowCursor")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial void HideWindowCursor(Window window); - - [LibraryImport("Elemental.Native", EntryPoint = "ElemGetWindowCursorPosition")] - [UnmanagedCallConv(CallConvs = new[] { typeof(CallConvCdecl) })] - internal static partial WindowCursorPosition GetWindowCursorPosition(Window window); - -} diff --git a/bindings/dotnet/src/Elemental_REF/ConsoleLogHandler.cs b/bindings/dotnet/src/Elemental_REF/ConsoleLogHandler.cs deleted file mode 100644 index 3f0ee50a..00000000 --- a/bindings/dotnet/src/Elemental_REF/ConsoleLogHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -namespace Elemental; - -/// -/// Class that contains a default log handler implementation. -/// -public static class DefaultLogHandlers -{ - /// - /// Default console log handler. - /// - /// Message type - /// Category - /// Function - /// Message - public static void ConsoleLogHandler(LogMessageType messageType, LogMessageCategory category, ReadOnlySpan function, ReadOnlySpan message) - { - var mainForegroundColor = messageType switch - { - LogMessageType.Warning => ConsoleColor.Yellow, - LogMessageType.Error => ConsoleColor.Red, - _ => ConsoleColor.Gray - }; - - Console.Write("["); - Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write($"{category}"); - Console.ForegroundColor = ConsoleColor.Gray; - Console.Write("]"); - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write($" {System.Text.UTF8Encoding.UTF8.GetString(function)}"); - - Console.ForegroundColor = mainForegroundColor; - Console.WriteLine($" {messageType}: {System.Text.UTF8Encoding.UTF8.GetString(message)}"); - Console.ForegroundColor = ConsoleColor.Gray; - } -} diff --git a/bindings/dotnet/src/Elemental_REF/Globals.cs b/bindings/dotnet/src/Elemental_REF/Globals.cs deleted file mode 100644 index 01008824..00000000 --- a/bindings/dotnet/src/Elemental_REF/Globals.cs +++ /dev/null @@ -1,7 +0,0 @@ -global using System.Numerics; -global using System.Runtime.InteropServices; -global using System.Runtime.InteropServices.Marshalling; -global using System.Runtime.CompilerServices; -global using System.Text; - -[assembly:System.Runtime.CompilerServices.DisableRuntimeMarshalling] diff --git a/bindings/dotnet/src/Elemental_REF/IApplicationService.cs b/bindings/dotnet/src/Elemental_REF/IApplicationService.cs deleted file mode 100644 index d55305ef..00000000 --- a/bindings/dotnet/src/Elemental_REF/IApplicationService.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Elemental; - -/// -/// Defines an interface for Application services. -/// -public interface IApplicationService -{ - /// - /// Configures a custom log handler for processing log messages generated by the application. - /// - /// The function to call when a log message is generated. - void ConfigureLogHandler(LogHandler logHandler); - - /// - /// Retrieves system-related information, such as platform and application path. - /// - /// A structure containing system information. - SystemInfo GetSystemInfo(); - - /// - /// Starts the execution of an application with specified parameters. - /// - /// Configuration and handlers for the application lifecycle. - /// Status code indicating success or error. - int RunApplication(in RunApplicationParameters parameters) where T : unmanaged; - - /// - /// Exits the application, performing necessary cleanup. - /// - /// Exit code of the application. - void ExitApplication(int exitCode); - - /// - /// Creates a window with the specified options or default settings if none are provided. - /// - /// Configuration options for the window; NULL for defaults. - /// A handle to the newly created window. - Window CreateWindow(in WindowOptions options = default); - - /// - /// Releases resources associated with a window. - /// - /// Handle to the window to be freed. - void FreeWindow(Window window); - - /// - /// Gets the render size of a window, accounting for DPI scaling. - /// - /// The window instance. - /// Render size of the window. - WindowSize GetWindowRenderSize(Window window); - - /// - /// Sets a window's title. - /// - /// The window instance. - /// New title for the window. - void SetWindowTitle(Window window, ReadOnlySpan title); - - /// - /// Changes the state of a window (e.g., minimize, maximize). - /// - /// The window instance. - /// New state for the window. - void SetWindowState(Window window, WindowState windowState); - - /// - /// TODO: Comments -///TODO: Make sure the coordinates are consistent accross all platforms - /// - void ShowWindowCursor(Window window); - - void HideWindowCursor(Window window); - - WindowCursorPosition GetWindowCursorPosition(Window window); -} diff --git a/bindings/dotnet/src/Elemental_REF/LogHandler.cs b/bindings/dotnet/src/Elemental_REF/LogHandler.cs deleted file mode 100644 index 176a2329..00000000 --- a/bindings/dotnet/src/Elemental_REF/LogHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -namespace Elemental; - -/// -/// Defines a function pointer type for log handling. -/// -/// The type of the log message. -/// The category of the log message. -/// The function where the log was triggered. -/// The log message. -[NativeMarshalling(typeof(LogHandlerMarshaller))] -public delegate void LogHandler(LogMessageType messageType, LogMessageCategory category, ReadOnlySpan function, ReadOnlySpan message); - -[CustomMarshaller(typeof(LogHandler), MarshalMode.ManagedToUnmanagedIn, typeof(LogHandlerMarshaller))] -internal static unsafe class LogHandlerMarshaller -{ - internal sealed unsafe record InterceptorEntry - { - public required LogHandler Callback { get; init; } - //public required GCHandle Handle { get; init; } - } - - private static InterceptorEntry? _interceptorEntry; - - private static unsafe void Interceptor(LogMessageType messageType, LogMessageCategory category, byte* function, byte* message) - { - if (_interceptorEntry == null || function == null || message == null) - { - return; - } - - var functionCounter = 0; - var functionPointer = (byte*)function; - - while (functionPointer[functionCounter] != 0) - { - functionCounter++; - } - - functionCounter++; - - var messageCounter = 0; - var messagePointer = (byte*)message; - - while (messagePointer[messageCounter] != 0) - { - messageCounter++; - } - - messageCounter++; - - _interceptorEntry.Callback(messageType, category, new ReadOnlySpan(function, functionCounter), new ReadOnlySpan(message, messageCounter)); - } - - public static nint ConvertToUnmanaged(LogHandler managed) - { - // TODO: Unallocate handle - //var interceptorDelegate = Interceptor; - //var handle = GCHandle.Alloc(interceptorDelegate); - //var unmanaged = Marshal.GetFunctionPointerForDelegate(interceptorDelegate); - - // TODO: Try to avoid all of that - delegate* unmanaged = &Interceptor; - - _interceptorEntry = new InterceptorEntry { Callback = managed }; - return (nint)unmanaged; - } - - public static void Free(nint _) - { - } -} diff --git a/bindings/dotnet/src/Elemental_REF/LogMessageCategory.cs b/bindings/dotnet/src/Elemental_REF/LogMessageCategory.cs deleted file mode 100644 index e49ad889..00000000 --- a/bindings/dotnet/src/Elemental_REF/LogMessageCategory.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Elemental; - -/// -/// Categorizes log messages by their related system components. -/// -public enum LogMessageCategory -{ - /// - /// Assertions and checks. - /// - Assert = 0, - - /// - /// Memory allocation and management. - /// - Memory = 1, - - /// - /// General application behavior. - /// - Application = 2, - - /// - /// Graphics system-related messages. - /// - Graphics = 3, - - /// - /// Input system-related messages. - /// - Inputs = 4 -} diff --git a/bindings/dotnet/src/Elemental_REF/LogMessageType.cs b/bindings/dotnet/src/Elemental_REF/LogMessageType.cs deleted file mode 100644 index a8eb2eae..00000000 --- a/bindings/dotnet/src/Elemental_REF/LogMessageType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Elemental; - -/// -/// Defines the types of log messages that can be generated. -/// -public enum LogMessageType -{ - /// - /// Debugging message. - /// - Debug = 0, - - /// - /// Warning message. - /// - Warning = 1, - - /// - /// Error message. - /// - Error = 2 -} diff --git a/bindings/dotnet/src/Elemental_REF/Platform.cs b/bindings/dotnet/src/Elemental_REF/Platform.cs deleted file mode 100644 index d578e044..00000000 --- a/bindings/dotnet/src/Elemental_REF/Platform.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Lists the platforms supported by the library. -/// -public enum Platform -{ - /// - /// Microsoft Windows platform. - /// - Windows = 0, - - /// - /// Apple macOS platform. - /// - MacOS = 1, - - /// - /// Apple iOS platform. - /// - iOS = 2, - - /// - /// Linux platform. - /// - Linux = 3 -} diff --git a/bindings/dotnet/src/Elemental_REF/RunApplicationParameters.cs b/bindings/dotnet/src/Elemental_REF/RunApplicationParameters.cs deleted file mode 100644 index 7ef786d2..00000000 --- a/bindings/dotnet/src/Elemental_REF/RunApplicationParameters.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Elemental; - -/// -/// Holds parameters for running an application, including initialization and cleanup routines. -/// -public ref struct RunApplicationParameters where T: unmanaged -{ - /// - /// Name of the application. - /// - public ReadOnlySpan ApplicationName { get; set; } - - /// - /// Function called at application startup. - /// - public ApplicationHandler InitHandler { get; set; } - - /// - /// Function called at application termination. - /// - public ApplicationHandler FreeHandler { get; set; } - - /// - /// Custom user data passed to handler functions. - /// - public T Payload { get; set; } -} - -internal unsafe struct RunApplicationParametersUnsafe -{ - public byte* ApplicationName { get; set; } - - public nint InitHandler { get; set; } - - public nint FreeHandler { get; set; } - - public nint Payload; -} - diff --git a/bindings/dotnet/src/Elemental_REF/SpanUnsafe.cs b/bindings/dotnet/src/Elemental_REF/SpanUnsafe.cs deleted file mode 100644 index e966f8ad..00000000 --- a/bindings/dotnet/src/Elemental_REF/SpanUnsafe.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Elemental; - -internal unsafe record struct SpanUnsafe where T : struct -{ - internal nuint Items { get; set; } - internal int Length { get; set; } -} diff --git a/bindings/dotnet/src/Elemental_REF/SystemInfo.cs b/bindings/dotnet/src/Elemental_REF/SystemInfo.cs deleted file mode 100644 index f3ee7822..00000000 --- a/bindings/dotnet/src/Elemental_REF/SystemInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace Elemental; - -/// -/// Contains information about the system, useful for tailoring application behavior. -/// -public ref struct SystemInfo -{ - /// - /// Operating system platform. - /// - public Platform Platform { get; set; } - - /// - /// Installation path of the application. - /// - public ReadOnlySpan ApplicationPath { get; set; } - - /// - /// Whether the application supports multiple windows. - /// - public bool SupportMultiWindows { get; set; } -} - -internal unsafe struct SystemInfoUnsafe -{ - public Platform Platform { get; set; } - - public byte* ApplicationPath { get; set; } - - public bool SupportMultiWindows { get; set; } -} - diff --git a/bindings/dotnet/src/Elemental_REF/Window.cs b/bindings/dotnet/src/Elemental_REF/Window.cs deleted file mode 100644 index 6b616a12..00000000 --- a/bindings/dotnet/src/Elemental_REF/Window.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Elemental; - -/// -/// Represents a handle to an elemental window. -/// -public readonly record struct Window : IDisposable -{ - private UInt64 Value { get; } - - /// - /// Disposes the handler. - /// - public void Dispose() - { - ApplicationServiceInterop.FreeWindow(this); - } -} diff --git a/bindings/dotnet/src/Elemental_REF/WindowCursorPosition.cs b/bindings/dotnet/src/Elemental_REF/WindowCursorPosition.cs deleted file mode 100644 index 6a67a13a..00000000 --- a/bindings/dotnet/src/Elemental_REF/WindowCursorPosition.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Elemental; - -/// -/// TODO: Comments -/// -public ref struct WindowCursorPosition -{ - public uint X { get; set; } - - public uint Y { get; set; } -} diff --git a/bindings/dotnet/src/Elemental_REF/WindowOptions.cs b/bindings/dotnet/src/Elemental_REF/WindowOptions.cs deleted file mode 100644 index 06209487..00000000 --- a/bindings/dotnet/src/Elemental_REF/WindowOptions.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Elemental; - -/// -/// Defines options for creating a new window. -/// -public ref struct WindowOptions -{ - /// - /// Title of the window. - /// - public ReadOnlySpan Title { get; set; } - - /// - /// Width of the window in pixels. - /// - public uint Width { get; set; } - - /// - /// Height of the window in pixels. - /// - public uint Height { get; set; } - - /// - /// Initial state of the window. - /// - public WindowState WindowState { get; set; } - - /// - /// True if the cursor should be hidden. - /// - public bool IsCursorHidden { get; set; } -} - -internal unsafe struct WindowOptionsUnsafe -{ - public byte* Title { get; set; } - - public uint Width { get; set; } - - public uint Height { get; set; } - - public WindowState WindowState { get; set; } - - public bool IsCursorHidden { get; set; } -} - diff --git a/bindings/dotnet/src/Elemental_REF/WindowSize.cs b/bindings/dotnet/src/Elemental_REF/WindowSize.cs deleted file mode 100644 index a9708ac8..00000000 --- a/bindings/dotnet/src/Elemental_REF/WindowSize.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Contains detailed information about the size and scaling factors of a window's render area. -/// -public ref struct WindowSize -{ - /// - /// Width of the window's render area in pixels. - /// - public uint Width { get; set; } - - /// - /// Height of the window's render area in pixels. - /// - public uint Height { get; set; } - - /// - /// UI scale factor, typically used for DPI adjustments. - /// - public float UIScale { get; set; } - - /// - /// Current state of the window. - /// - public WindowState WindowState { get; set; } -} diff --git a/bindings/dotnet/src/Elemental_REF/WindowState.cs b/bindings/dotnet/src/Elemental_REF/WindowState.cs deleted file mode 100644 index 649a4749..00000000 --- a/bindings/dotnet/src/Elemental_REF/WindowState.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Elemental; - -/// -/// Enumerates the possible states of a window. -/// -public enum WindowState -{ - /// - /// Window is in normal state. - /// - Normal = 0, - - /// - /// Window is minimized. - /// - Minimized = 1, - - /// - /// Window is maximized. - /// - Maximized = 2, - - /// - /// Window is in full screen mode. - /// - FullScreen = 3 -} From 8ee0b5157d045e1aca9c5e9906e46ca7d369e52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 7 Jan 2025 09:50:20 +0100 Subject: [PATCH 58/93] Update external dep --- external/DirectX-Headers | 2 +- external/Vulkan-Headers | 2 +- external/cimgui | 2 +- external/meshoptimizer | 2 +- external/utest | 2 +- external/volk | 2 +- external/xxHash | 2 +- tests/GraphicsTests/ResourceIOTests.cpp | 75 +++++++++++++++++++++++++ 8 files changed, 82 insertions(+), 7 deletions(-) diff --git a/external/DirectX-Headers b/external/DirectX-Headers index 5c45253f..48a76297 160000 --- a/external/DirectX-Headers +++ b/external/DirectX-Headers @@ -1 +1 @@ -Subproject commit 5c45253f7d5dd2f967f45f3f48088890d72640a5 +Subproject commit 48a762973271c5a75869946bf1fdbc489a628a5c diff --git a/external/Vulkan-Headers b/external/Vulkan-Headers index 577baa05..d4a196d8 160000 --- a/external/Vulkan-Headers +++ b/external/Vulkan-Headers @@ -1 +1 @@ -Subproject commit 577baa05033cf1d9236b3d078ca4b3269ed87a2b +Subproject commit d4a196d8c84e032d27f999adcea3075517c1c97f diff --git a/external/cimgui b/external/cimgui index 3d5b2e76..e3b48a15 160000 --- a/external/cimgui +++ b/external/cimgui @@ -1 +1 @@ -Subproject commit 3d5b2e76503942b7d2ea3d6fb5472fe98077a21c +Subproject commit e3b48a15f098d8f569f266abd96807094f827624 diff --git a/external/meshoptimizer b/external/meshoptimizer index 2f7663bb..4affad04 160000 --- a/external/meshoptimizer +++ b/external/meshoptimizer @@ -1 +1 @@ -Subproject commit 2f7663bb321b6242674165a4feef242139ea85b7 +Subproject commit 4affad044571506a5724c9a6f15424f43e86f731 diff --git a/external/utest b/external/utest index ab627881..25c4a9d7 160000 --- a/external/utest +++ b/external/utest @@ -1 +1 @@ -Subproject commit ab62788136a5635e04675322ece84b64d938bd76 +Subproject commit 25c4a9d78df0fb12e6c5364bc811f7f675e9dcb3 diff --git a/external/volk b/external/volk index 01986ac8..0b17a763 160000 --- a/external/volk +++ b/external/volk @@ -1 +1 @@ -Subproject commit 01986ac85fa2e5c70df09aeae9c907e27c5d50b2 +Subproject commit 0b17a763ba5643e32da1b2152f8140461b3b7345 diff --git a/external/xxHash b/external/xxHash index f91df681..e626a72b 160000 --- a/external/xxHash +++ b/external/xxHash @@ -1 +1 @@ -Subproject commit f91df681b034d78c7ce87de66f0f78a1e40e7bfb +Subproject commit e626a72bc2321cd320e953a0ccf1584cad60f363 diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index 19e2c103..180abbc9 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -419,3 +419,78 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandList free(data); } + +UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + + ElemGraphicsHeapOptions options = + { + .HeapType = ElemGraphicsHeapType_GpuUpload + }; + + const auto width = 1024u; + const auto height = 1024u; + const auto mipLevelCount = 11u; + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(50), &options); + // TODO: Change that to R8G8B8A8 + auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, mipLevelCount, ElemGraphicsFormat_B8G8R8A8, ElemGraphicsResourceUsage_Read, nullptr); + auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + uint8_t* mipData[mipLevelCount]; + + for (uint32_t i = 0; i < mipLevelCount; i++) + { + auto currentWidth = max(1u, width >> i); + auto currentHeight = max(1u, height >> i); + + mipData[i] = (uint8_t*)malloc(currentWidth * currentHeight * 4); + + for (uint32_t j = 0; j < currentWidth * currentHeight; j++) + { + // TODO: Colors + ((uint32_t*)mipData[i])[j] = 0x000000; + } + } + + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + auto commandList = ElemGetCommandList(commandQueue, nullptr); + + // Act + for (uint32_t i = 0; i < mipLevelCount; i++) + { + auto currentWidth = max(1u, width >> i); + auto currentHeight = max(1u, height >> i); + + ElemCopyDataToGraphicsResourceParameters parameters = + { + .Resource = resource, + .TextureMipLevel = i, + .SourceType = ElemCopyDataSourceType_Memory, + .SourceMemoryData = { .Items = mipData[i], .Length = currentWidth * currentHeight * 4 } + }; + + ElemCopyDataToGraphicsResource(commandList, ¶meters); + } + + // Assert + ElemCommitCommandList(commandList); + + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + ElemWaitForFenceOnCpu(fence); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsResource(resource, nullptr); + ElemFreeGraphicsHeap(graphicsHeap); + ElemFreeGraphicsDevice(graphicsDevice); + + for (uint32_t i = 0; i < mipLevelCount; i++) + { + free(mipData[i]); + } + + ASSERT_LOG_NOERROR(); + + // TODO: Assert the data for each mips +} From ae6f91f7e399584455559e4c6019c20e2c0e8c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 7 Jan 2025 14:42:32 +0100 Subject: [PATCH 59/93] Rework external makefiles --- external/CMakeLists.txt | 137 +----------------- external/DirectX12/CMakeLists.txt | 25 ++++ external/DirectXShaderCompiler/CMakeLists.txt | 52 +++++++ external/MetalShaderConverter/CMakeLists.txt | 38 +++++ 4 files changed, 121 insertions(+), 131 deletions(-) create mode 100644 external/DirectX12/CMakeLists.txt create mode 100644 external/DirectXShaderCompiler/CMakeLists.txt create mode 100644 external/MetalShaderConverter/CMakeLists.txt diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index e817d78b..d7bdc4e5 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,118 +1,15 @@ -# TODO: Make a folder per bin external dep - #======================================================================= # DirectX Shader Compiler #======================================================================= -set(DXC_VERSION_CUSTOM "v2024-07-27") - -add_library(dxc INTERFACE) - -if(WIN32) - get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "windows_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/include/dxc/) - - function(copy_dxc_to_target target) - file(GLOB DXC_DLLS - "${CMAKE_BINARY_DIR}/external/shader-compilers/dxc/lib/*.dll") - foreach(DLL ${DXC_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() -elseif(LINUX) - get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "linux_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/include/dxc/) - target_include_directories(dxc INTERFACE ./DirectX-Headers/include/directx/) - - function(copy_dxc_to_target target) - file(GLOB DXC_DLLS - "${CMAKE_BINARY_DIR}/external/shader-compilers/dxc/lib/*.so") - foreach(DLL ${DXC_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() -elseif(NOT BUILD_FOR_IOS) - get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "macos_dxc_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/dxc/include/dxc/) - target_include_directories(dxc INTERFACE ./DirectX-Headers/include/directx/) - - function(copy_dxc_to_target target) - file(GLOB DXC_DLLS - "${CMAKE_BINARY_DIR}/external/shader-compilers/dxc/lib/*.dylib") - foreach(DLL ${DXC_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() +if(NOT BUILD_FOR_IOS) + add_subdirectory(./DirectXShaderCompiler) endif() #======================================================================= # Metal Shader Converter #======================================================================= -if (WIN32 OR APPLE) - set(METAL_SHADER_CONVERTER_VERSION "v2024-06-11") - - add_library(metal-shader-converter INTERFACE) - target_include_directories(metal-shader-converter INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/MetalShaderConverter/include/) - target_link_directories(metal-shader-converter INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/MetalShaderConverter/lib/) - target_link_libraries(metal-shader-converter INTERFACE metalirconverter) - - if(WIN32) - get_github_release("double-buffer/shader-compilers-bin" ${METAL_SHADER_CONVERTER_VERSION} "windows_MetalShaderConverter_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/MetalShaderConverter/") - - function(copy_MetalShaderConverter_to_target target) - file(GLOB DXC_DLLS - "${CMAKE_BINARY_DIR}/external/shader-compilers/MetalShaderConverter/lib/*.dll") - foreach(DLL ${DXC_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() - elseif(NOT BUILD_FOR_IOS) - get_github_release("double-buffer/shader-compilers-bin" ${METAL_SHADER_CONVERTER_VERSION} "macos_MetalShaderConverter_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/MetalShaderConverter/") - - function(copy_MetalShaderConverter_to_target target) - file(GLOB DXC_DLLS - "${CMAKE_BINARY_DIR}/external/shader-compilers/MetalShaderConverter/lib/*.dylib") - foreach(DLL ${DXC_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() - endif() -endif() - -#======================================================================= -# SPIRV-Cross Compiler -#======================================================================= -set(SPIRV_CROSS_VERSION "v2024-06-11") - -add_library(spirv-cross INTERFACE) -target_include_directories(spirv-cross INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/spirv-cross/include/) -target_link_libraries(spirv-cross INTERFACE spirv-cross-core spirv-cross-cpp spirv-cross-msl spirv-cross-glsl spirv-cross-reflect spirv-cross-util) - -if(WIN32) - get_github_release("double-buffer/shader-compilers-bin" ${SPIRV_CROSS_VERSION} "windows_spirv-cross_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/spirv-cross/") - target_link_directories(spirv-cross INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/spirv-cross/lib/lib/) -elseif(NOT BUILD_FOR_IOS) - get_github_release("double-buffer/shader-compilers-bin" ${SPIRV_CROSS_VERSION} "macos_spirv-cross_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/spirv-cross/") - target_link_directories(spirv-cross INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/shader-compilers/spirv-cross/lib/) +if(NOT BUILD_FOR_IOS) + add_subdirectory(./MetalShaderConverter) endif() #======================================================================= @@ -170,36 +67,14 @@ endif() # Direct3D12 Runtime #======================================================================= if(WIN32) - set(DIRECT3D12_VERSION "1.614.0") - - add_library(Direct3D12 INTERFACE) - - download_and_extract_nuget_package(Direct3D12 "Microsoft.Direct3D.D3D12" ${DIRECT3D12_VERSION}) - target_link_libraries(Direct3D12 INTERFACE dxgi dxguid) - - # TODO: Do we really need that? - target_link_libraries(Direct3D12 INTERFACE d3d12) - - function(copy_direct3d12_to_target target) - # TODO: To Fix - set(DIRECT3D12_VERSION "1.614.0") - file(GLOB DIRECT3D12_DLLS - "${CMAKE_BINARY_DIR}/external/packages/Microsoft.Direct3D.D3D12_${DIRECT3D12_VERSION}/build/native/bin/x64/*.dll") - foreach(DLL ${DIRECT3D12_DLLS}) - get_filename_component(DLL_NAME "${DLL}" NAME) - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${DLL}" - "$/${DLL_NAME}") - endforeach() - endfunction() + add_subdirectory(./DirectX12) endif() #======================================================================= # Shader Compilers #======================================================================= add_library(tools_shader_compilers INTERFACE) -target_link_libraries(tools_shader_compilers INTERFACE spirv-cross dxc) +target_link_libraries(tools_shader_compilers INTERFACE dxc) if(WIN32 OR APPLE) target_link_libraries(tools_shader_compilers INTERFACE metal-shader-converter) diff --git a/external/DirectX12/CMakeLists.txt b/external/DirectX12/CMakeLists.txt new file mode 100644 index 00000000..697b9b05 --- /dev/null +++ b/external/DirectX12/CMakeLists.txt @@ -0,0 +1,25 @@ +if(WIN32) + set(DIRECT3D12_VERSION "1.614.0") + + add_library(Direct3D12 INTERFACE) + + download_and_extract_nuget_package(Direct3D12 "Microsoft.Direct3D.D3D12" ${DIRECT3D12_VERSION}) + target_link_libraries(Direct3D12 INTERFACE dxgi dxguid) + + # TODO: Do we really need that? + target_link_libraries(Direct3D12 INTERFACE d3d12) + + function(copy_direct3d12_to_target target) + # TODO: To Fix + set(DIRECT3D12_VERSION "1.614.0") + file(GLOB DIRECT3D12_DLLS + "${CMAKE_BINARY_DIR}/external/DirectX12/packages/Microsoft.Direct3D.D3D12_${DIRECT3D12_VERSION}/build/native/bin/x64/*.dll") + foreach(DLL ${DIRECT3D12_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() +endif() diff --git a/external/DirectXShaderCompiler/CMakeLists.txt b/external/DirectXShaderCompiler/CMakeLists.txt new file mode 100644 index 00000000..6093e245 --- /dev/null +++ b/external/DirectXShaderCompiler/CMakeLists.txt @@ -0,0 +1,52 @@ +set(DXC_VERSION_CUSTOM "v2025-01-07") + +add_library(dxc INTERFACE) + +if(WIN32) + get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "windows_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + + function(copy_dxc_to_target target) + file(GLOB DXC_DLLS + "${CMAKE_BINARY_DIR}/external/DirectXShaderCompiler/dxc/lib/*.dll") + foreach(DLL ${DXC_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() +elseif(LINUX) + get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "linux_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + target_include_directories(dxc INTERFACE ../DirectX-Headers/include/directx/) + + function(copy_dxc_to_target target) + file(GLOB DXC_DLLS + "${CMAKE_BINARY_DIR}/external/DirectXShaderCompiler/dxc/lib/*.so") + foreach(DLL ${DXC_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() +elseif(NOT BUILD_FOR_IOS) + get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "macos_dxc_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + target_include_directories(dxc INTERFACE ../DirectX-Headers/include/directx/) + + function(copy_dxc_to_target target) + file(GLOB DXC_DLLS + "${CMAKE_BINARY_DIR}/external/DirectXShaderCompiler/dxc/lib/*.dylib") + foreach(DLL ${DXC_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() +endif() diff --git a/external/MetalShaderConverter/CMakeLists.txt b/external/MetalShaderConverter/CMakeLists.txt new file mode 100644 index 00000000..2f9e46c4 --- /dev/null +++ b/external/MetalShaderConverter/CMakeLists.txt @@ -0,0 +1,38 @@ +if (WIN32 OR APPLE) + set(METAL_SHADER_CONVERTER_VERSION "v2024-06-11") + + add_library(metal-shader-converter INTERFACE) + target_include_directories(metal-shader-converter INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/MetalShaderConverter/include/) + target_link_directories(metal-shader-converter INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/MetalShaderConverter/lib/) + target_link_libraries(metal-shader-converter INTERFACE metalirconverter) + + if(WIN32) + get_github_release("double-buffer/shader-compilers-bin" ${METAL_SHADER_CONVERTER_VERSION} "windows_MetalShaderConverter_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/MetalShaderConverter/") + + function(copy_MetalShaderConverter_to_target target) + file(GLOB DXC_DLLS + "${CMAKE_BINARY_DIR}/external/MetalShaderConverter/MetalShaderConverter/lib/*.dll") + foreach(DLL ${DXC_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() + elseif(NOT BUILD_FOR_IOS) + get_github_release("double-buffer/shader-compilers-bin" ${METAL_SHADER_CONVERTER_VERSION} "macos_MetalShaderConverter_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/MetalShaderConverter/") + + function(copy_MetalShaderConverter_to_target target) + file(GLOB DXC_DLLS + "${CMAKE_BINARY_DIR}/external/MetalShaderConverter/MetalShaderConverter/lib/*.dylib") + foreach(DLL ${DXC_DLLS}) + get_filename_component(DLL_NAME "${DLL}" NAME) + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${DLL}" + "$/${DLL_NAME}") + endforeach() + endfunction() + endif() +endif() From 1a26b8c9330e824eb549f512bcf489351b9d2ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 7 Jan 2025 16:32:25 +0100 Subject: [PATCH 60/93] Refactor graphics tests --- external/DirectX12/CMakeLists.txt | 4 +- samples/Elemental/06-HelloMesh/main.c | 23 +------- tests/GraphicsTests/Data/Assert.hlsl | 19 +++++-- tests/GraphicsTests/GraphicsTests.cpp | 9 +++- tests/GraphicsTests/GraphicsTests.h | 3 +- tests/GraphicsTests/RenderingTests.cpp | 57 +++++++++++--------- tests/GraphicsTests/ResourceIOTests.cpp | 71 +++++++++++++++++-------- tests/GraphicsTests/ShaderTests.cpp | 25 +++++---- 8 files changed, 123 insertions(+), 88 deletions(-) diff --git a/external/DirectX12/CMakeLists.txt b/external/DirectX12/CMakeLists.txt index 697b9b05..56a72b57 100644 --- a/external/DirectX12/CMakeLists.txt +++ b/external/DirectX12/CMakeLists.txt @@ -1,5 +1,5 @@ if(WIN32) - set(DIRECT3D12_VERSION "1.614.0") + set(DIRECT3D12_VERSION "1.614.1") add_library(Direct3D12 INTERFACE) @@ -11,7 +11,7 @@ if(WIN32) function(copy_direct3d12_to_target target) # TODO: To Fix - set(DIRECT3D12_VERSION "1.614.0") + set(DIRECT3D12_VERSION "1.614.1") file(GLOB DIRECT3D12_DLLS "${CMAKE_BINARY_DIR}/external/DirectX12/packages/Microsoft.Direct3D.D3D12_${DIRECT3D12_VERSION}/build/native/bin/x64/*.dll") foreach(DLL ${DIRECT3D12_DLLS}) diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/06-HelloMesh/main.c index e29fa18e..f225f286 100644 --- a/samples/Elemental/06-HelloMesh/main.c +++ b/samples/Elemental/06-HelloMesh/main.c @@ -34,7 +34,6 @@ typedef struct ElemWindow Window; ElemGraphicsDevice GraphicsDevice; ElemCommandQueue CommandQueue; - ElemGraphicsHeap GraphicsHeap; uint32_t CurrentHeapOffset; ElemFence LastExecutionFence; ElemSwapChain SwapChain; @@ -67,21 +66,6 @@ void CreateDepthBuffer(ApplicationPayload* applicationPayload, uint32_t width, u applicationPayload->DepthBuffer = ElemCreateGraphicsResource(applicationPayload->DepthBufferHeap, 0, &resourceInfo); } -// TODO: To remove when IOQueues -void CreateAndUploadDataTemp(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, void* dataPointer, uint32_t sizeInBytes) -{ - // TODO: Alignment should be used with the offset before adding the size of the resource! - ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); - - applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); - *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); - applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; - - *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); - - ElemUploadGraphicsBufferData(*buffer, 0, (ElemDataSpan) { .Items = dataPointer, .Length = sizeInBytes }); -} - void InitSample(void* payload) { ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; @@ -105,10 +89,6 @@ void InitSample(void* payload) // TODO: For now we create a separate heap to avoid memory management applicationPayload->DepthBufferHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_Gpu }); - - // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues - // TODO: Having GPU Upload is still annoying 😞 - //applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(256)); CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); @@ -167,7 +147,8 @@ void FreeSample(void* payload) ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); - ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); + SampleFreeGpuMemory(&applicationPayload->GpuMemory); + ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); } diff --git a/tests/GraphicsTests/Data/Assert.hlsl b/tests/GraphicsTests/Data/Assert.hlsl index 86c3027a..137ece4e 100644 --- a/tests/GraphicsTests/Data/Assert.hlsl +++ b/tests/GraphicsTests/Data/Assert.hlsl @@ -2,27 +2,38 @@ struct Parameters { uint SourceBufferIndex; uint DestinationBufferIndex; + uint MipLevel; }; [[vk::push_constant]] Parameters parameters : register(b0); [shader("compute")] -[numthreads(16, 16, 1)] +[numthreads(8, 8, 1)] void CopyTexture(uint2 threadId: SV_DispatchThreadID) { Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; - destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); + float width; + float height; + float mipLevelCount; + sourceTexture.GetDimensions(parameters.MipLevel, width, height, mipLevelCount); + + destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); } [shader("compute")] -[numthreads(16, 16, 1)] +[numthreads(8, 8, 1)] void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) { Texture2D sourceTexture = ResourceDescriptorHeap[parameters.SourceBufferIndex]; RWStructuredBuffer destinationBuffer = ResourceDescriptorHeap[parameters.DestinationBufferIndex]; - destinationBuffer[threadId.y * 16 + threadId.x] = sourceTexture.Load(uint3(threadId, 0)); + float width; + float height; + float mipLevelCount; + sourceTexture.GetDimensions(parameters.MipLevel, width, height, mipLevelCount); + + destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); } diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index 78525987..b58120bf 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -352,7 +352,12 @@ void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer) TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage) { - auto textureInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, 1, format, usage, nullptr); + return TestCreateGpuTexture(graphicsDevice, width, height, 1, format, usage); +} + +TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, uint32_t mipLevels, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage) +{ + auto textureInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, mipLevels, format, usage, nullptr); auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, textureInfo.SizeInBytes, nullptr); auto texture = ElemCreateGraphicsResource(graphicsHeap, 0, &textureInfo); auto textureReadDescriptor = ElemCreateGraphicsResourceDescriptor(texture, ElemGraphicsResourceDescriptorUsage_Read, nullptr); @@ -397,7 +402,7 @@ void TestDispatchCompute(ElemCommandList commandList, ElemPipelineState pipeline } template -void TestDispatchComputeForReadbackBuffer(ElemGraphicsDevice graphicsDevice, ElemCommandQueue commandQueue, const char* shaderName, const char* function, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, const T* parameters) +void TestDispatchComputeForShader(ElemGraphicsDevice graphicsDevice, ElemCommandQueue commandQueue, const char* shaderName, const char* function, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, const T* parameters) { auto pipelineState = TestOpenComputeShader(graphicsDevice, shaderName, function); diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 3cef28bf..61f871df 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -185,13 +185,14 @@ TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t si void TestFreeGpuBuffer(TestGpuBuffer gpuBuffer); TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage); +TestGpuTexture TestCreateGpuTexture(ElemGraphicsDevice graphicsDevice, uint32_t width, uint32_t height, uint32_t mipLevels, ElemGraphicsFormat format, ElemGraphicsResourceUsage usage); void TestFreeGpuTexture(TestGpuTexture texture); template void TestDispatchCompute(ElemCommandList commandList, ElemPipelineState pipelineState, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, std::initializer_list parameters); template -void TestDispatchComputeForReadbackBuffer(ElemGraphicsDevice graphicsDevice, ElemCommandQueue commandQueue, const char* shaderName, const char* function, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, const T* parameters); +void TestDispatchComputeForShader(ElemGraphicsDevice graphicsDevice, ElemCommandQueue commandQueue, const char* shaderName, const char* function, uint32_t threadGroupSizeX, uint32_t threadGroupSizeY, uint32_t threadGroupSizeZ, const T* parameters); void TestBarrierCheckSyncTypeToString(char* destination, ElemGraphicsResourceBarrierSyncType syncType); void TestBarrierCheckAccessTypeToString(char* destination, ElemGraphicsResourceBarrierAccessType accessType); diff --git a/tests/GraphicsTests/RenderingTests.cpp b/tests/GraphicsTests/RenderingTests.cpp index 53ab534e..8daa4eff 100644 --- a/tests/GraphicsTests/RenderingTests.cpp +++ b/tests/GraphicsTests/RenderingTests.cpp @@ -15,7 +15,8 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); // Act ElemRenderPassRenderTarget renderPassRenderTarget = @@ -42,9 +43,9 @@ UTEST(Rendering, RenderPassClearRenderTarget) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -63,7 +64,8 @@ UTEST(Rendering, RenderPassClearDepthBuffer) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + auto textureSize = 16u; + auto depthBuffer = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); // Act ElemBeginRenderPassParameters parameters = @@ -84,9 +86,9 @@ UTEST(Rendering, RenderPassClearDepthBuffer) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)depthBuffer.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTextureFloat", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTextureFloat", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -111,7 +113,8 @@ UTEST(Rendering, DispatchMesh) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -148,9 +151,9 @@ UTEST(Rendering, DispatchMesh) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -170,7 +173,8 @@ UTEST(Rendering, BeginRenderPass_SetViewport) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -220,9 +224,9 @@ UTEST(Rendering, BeginRenderPass_SetViewport) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -232,7 +236,7 @@ UTEST(Rendering, BeginRenderPass_SetViewport) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, textureSize, 2, 2, 4, 4, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); @@ -245,7 +249,8 @@ UTEST(Rendering, BeginRenderPass_SetScissorRectangle) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -295,9 +300,9 @@ UTEST(Rendering, BeginRenderPass_SetScissorRectangle) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -307,7 +312,7 @@ UTEST(Rendering, BeginRenderPass_SetScissorRectangle) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, textureSize, 2, 2, 4, 4, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); @@ -320,7 +325,8 @@ UTEST(Rendering, SetViewport) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -366,9 +372,9 @@ UTEST(Rendering, SetViewport) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -378,7 +384,7 @@ UTEST(Rendering, SetViewport) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, textureSize, 2, 2, 4, 4, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); @@ -391,7 +397,8 @@ UTEST(Rendering, SetScissorRectangle) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -437,9 +444,9 @@ UTEST(Rendering, SetScissorRectangle) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -449,7 +456,7 @@ UTEST(Rendering, SetScissorRectangle) ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, 16, + ASSERT_COLOR_BUFFER_RECTANGLE(bufferData, textureSize, 2, 2, 4, 4, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f); diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index 180abbc9..484f350b 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -2,9 +2,16 @@ #include "GraphicsTests.h" #include "utest.h" +ElemVector3 TestMipColors[] = +{ + { 1.0f, 0.0f, 0.0f }, + { 1.0f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 1.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, +}; + // TODO: File source -// TODO: Textures: Check each mip map validity with different colors -// TODO: Check alignement after mip 5 // TODO: Big source (so that the upload heap can grow) // TODO: Test buffer offset and length if too much @@ -425,18 +432,11 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) // Arrange auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); - ElemGraphicsHeapOptions options = - { - .HeapType = ElemGraphicsHeapType_GpuUpload - }; - const auto width = 1024u; const auto height = 1024u; const auto mipLevelCount = 11u; - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, TestMegaBytesToBytes(50), &options); - // TODO: Change that to R8G8B8A8 - auto resourceInfo = ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, mipLevelCount, ElemGraphicsFormat_B8G8R8A8, ElemGraphicsResourceUsage_Read, nullptr); - auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); + + auto texture = TestCreateGpuTexture(graphicsDevice, width, height, mipLevelCount, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_Read); uint8_t* mipData[mipLevelCount]; @@ -445,12 +445,15 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) auto currentWidth = max(1u, width >> i); auto currentHeight = max(1u, height >> i); - mipData[i] = (uint8_t*)malloc(currentWidth * currentHeight * 4); + mipData[i] = (uint8_t*)malloc(currentWidth * currentHeight * 16); + auto testColor = TestMipColors[i % ARRAYSIZE(TestMipColors)]; - for (uint32_t j = 0; j < currentWidth * currentHeight; j++) + for (uint32_t j = 0; j < currentWidth * currentHeight * 4; j += 4) { - // TODO: Colors - ((uint32_t*)mipData[i])[j] = 0x000000; + ((float*)mipData[i])[j] = testColor.X; + ((float*)mipData[i])[j + 1] = testColor.Y; + ((float*)mipData[i])[j + 2] = testColor.Z; + ((float*)mipData[i])[j + 3] = 1.0f; } } @@ -465,7 +468,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) ElemCopyDataToGraphicsResourceParameters parameters = { - .Resource = resource, + .Resource = texture.Texture, .TextureMipLevel = i, .SourceType = ElemCopyDataSourceType_Memory, .SourceMemoryData = { .Items = mipData[i], .Length = currentWidth * currentHeight * 4 } @@ -480,17 +483,41 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - ElemFreeCommandQueue(commandQueue); - ElemFreeGraphicsResource(resource, nullptr); - ElemFreeGraphicsHeap(graphicsHeap); - ElemFreeGraphicsDevice(graphicsDevice); + ElemDataSpan outputMipData[mipLevelCount]; for (uint32_t i = 0; i < mipLevelCount; i++) { - free(mipData[i]); + auto currentWidth = max(1u, width >> i); + auto currentHeight = max(1u, height >> i); + + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, currentWidth * currentHeight * sizeof(float), ElemGraphicsHeapType_Readback); + uint32_t resourceIdList[] = { (uint32_t)texture.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor, i }; + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", (currentWidth + 7) / 8, (currentHeight + 7) / 8, 1, &resourceIdList); + + auto readbackBufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); + outputMipData[i].Items = (uint8_t*)malloc(readbackBufferData.Length); + outputMipData[i].Length = readbackBufferData.Length; + memcpy(outputMipData[i].Items, readbackBufferData.Items, readbackBufferData.Length); + + TestFreeGpuBuffer(readbackBuffer); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); } + TestFreeGpuTexture(texture); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); ASSERT_LOG_NOERROR(); - // TODO: Assert the data for each mips + for (uint32_t i = 0; i < mipLevelCount; i++) + { + auto outputMipDataItem = outputMipData[i]; + + for (uint32_t j = 0; j < outputMipDataItem.Length; j++) + { + ASSERT_EQ_MSG(outputMipDataItem.Items[j], mipData[i][j], "Test"); + } + + free(mipData[i]); + free(outputMipData[i].Items); + } } diff --git a/tests/GraphicsTests/ShaderTests.cpp b/tests/GraphicsTests/ShaderTests.cpp index 8d4cf2f2..d0d7aaa7 100644 --- a/tests/GraphicsTests/ShaderTests.cpp +++ b/tests/GraphicsTests/ShaderTests.cpp @@ -84,7 +84,7 @@ UTEST(Shader, DispatchCompute) auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 64 * sizeof(uint32_t), ElemGraphicsHeapType_Readback); // Act - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "ShaderTests.shader", "TestCompute", 1, 1, 1, &readbackBuffer.WriteDescriptor); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "ShaderTests.shader", "TestCompute", 1, 1, 1, &readbackBuffer.WriteDescriptor); // Assert auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); @@ -122,7 +122,8 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateFillAndCullMode) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -165,9 +166,9 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateFillAndCullMode) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -278,8 +279,9 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); - auto depthBuffer = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto depthBuffer = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_D32_FLOAT, ElemGraphicsResourceUsage_DepthStencil); ElemGraphicsPipelineStateRenderTarget psoRenderTarget { .Format = renderTarget.Format }; ElemGraphicsPipelineStateParameters psoParameters = @@ -338,9 +340,9 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateDepthCompare) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); @@ -441,7 +443,8 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateBlendState) auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); auto commandList = ElemGetCommandList(commandQueue, nullptr); - auto renderTarget = TestCreateGpuTexture(graphicsDevice, 16, 16, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); + auto textureSize = 16u; + auto renderTarget = TestCreateGpuTexture(graphicsDevice, textureSize, textureSize, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsResourceUsage_RenderTarget); ElemGraphicsPipelineStateRenderTarget psoRenderTarget = { @@ -498,9 +501,9 @@ UTEST_F_TEARDOWN(Shader_CompileGraphicsPipelineStateBlendState) auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); ElemWaitForFenceOnCpu(fence); - auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, 16 * 16 * 4 * sizeof(float), ElemGraphicsHeapType_Readback); + auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, textureSize * textureSize * 4 * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)renderTarget.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor }; - TestDispatchComputeForReadbackBuffer(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", 1, 1, 1, &resourceIdList); + TestDispatchComputeForShader(graphicsDevice, commandQueue, "Assert.shader", "CopyTexture", textureSize / 8, textureSize / 8, 1, &resourceIdList); auto bufferData = ElemDownloadGraphicsBufferData(readbackBuffer.Buffer, nullptr); TestFreeGpuBuffer(readbackBuffer); From ab7886419fd55f7b7bdebb0a61eff19bf3788a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 08:34:17 +0100 Subject: [PATCH 61/93] Change dir structure --- .github/workflows/build-ci.yml | 6 ------ .github/workflows/build.yml | 3 +-- doc/{ => DesignNotes}/Barriers.md | 0 doc/{ => DesignNotes}/IOCommands.md | 0 doc/{ => DesignNotes}/LatencyQuestions.md | 0 REFERENCES.MD => doc/references.md | 0 external/DirectXShaderCompiler/CMakeLists.txt | 4 ++-- .../Platforms/Apple/PreCompiledHeader.h | 2 ++ src/ElementalTools/UnityBuild.cpp | 4 ++++ tests/GraphicsTests/GraphicsTests.h | 3 +++ tests/GraphicsTests/ResourceIOTests.cpp | 12 ++++++------ 11 files changed, 18 insertions(+), 16 deletions(-) rename doc/{ => DesignNotes}/Barriers.md (100%) rename doc/{ => DesignNotes}/IOCommands.md (100%) rename doc/{ => DesignNotes}/LatencyQuestions.md (100%) rename REFERENCES.MD => doc/references.md (100%) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index aa22844d..44519815 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -17,12 +17,6 @@ jobs: with: platform: ${{ matrix.platform }} architecture: ${{ matrix.architecture }} - - # TODO: Do a build bindings sub workflow - #build_dotnet: - # uses: ./.github/workflows/build-dotnet.yml - #name: 'build-dotnet' - #secrets: inherit run_tests: strategy: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3a51004..a3cec024 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ on: jobs: build: runs-on: ${{ - (inputs.platform == 'osx' && 'macos-14') || + (inputs.platform == 'osx' && 'macos-latest') || (inputs.platform == 'linux' && 'ubuntu-latest') || (inputs.platform == 'win' && 'windows-latest') }} @@ -34,7 +34,6 @@ jobs: if: inputs.platform == 'linux' run: | sudo apt-get update - sudo apt-get install -y -V libgtk-4-dev - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master diff --git a/doc/Barriers.md b/doc/DesignNotes/Barriers.md similarity index 100% rename from doc/Barriers.md rename to doc/DesignNotes/Barriers.md diff --git a/doc/IOCommands.md b/doc/DesignNotes/IOCommands.md similarity index 100% rename from doc/IOCommands.md rename to doc/DesignNotes/IOCommands.md diff --git a/doc/LatencyQuestions.md b/doc/DesignNotes/LatencyQuestions.md similarity index 100% rename from doc/LatencyQuestions.md rename to doc/DesignNotes/LatencyQuestions.md diff --git a/REFERENCES.MD b/doc/references.md similarity index 100% rename from REFERENCES.MD rename to doc/references.md diff --git a/external/DirectXShaderCompiler/CMakeLists.txt b/external/DirectXShaderCompiler/CMakeLists.txt index 6093e245..8caa03af 100644 --- a/external/DirectXShaderCompiler/CMakeLists.txt +++ b/external/DirectXShaderCompiler/CMakeLists.txt @@ -19,7 +19,7 @@ if(WIN32) endfunction() elseif(LINUX) get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "linux_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/dxc/include/dxc/) target_include_directories(dxc INTERFACE ../DirectX-Headers/include/directx/) function(copy_dxc_to_target target) @@ -35,7 +35,7 @@ elseif(LINUX) endfunction() elseif(NOT BUILD_FOR_IOS) get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "macos_dxc_*_arm64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/dxc/include/dxc/) target_include_directories(dxc INTERFACE ../DirectX-Headers/include/directx/) function(copy_dxc_to_target target) diff --git a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h index e092c362..69e0ac66 100644 --- a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h @@ -8,6 +8,7 @@ #define ElemToolsAPI extern "C" __attribute__((visibility("default"))) #define AssertIfFailed(expression) SystemAssert(SUCCEEDED((expression))) #define AssertIfFailedReturnNullHandle(expression) SystemAssertReturnNullHandle(SUCCEEDED((expression))) + #define ComPtr CComPtr #undef _WIN32 #include @@ -39,6 +40,7 @@ #include "meshoptimizer.h" #include "fast_obj.h" #include "cgltf.h" + #include "stb_image.h" #include "stb_image_resize2.h" diff --git a/src/ElementalTools/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp index 515f86d1..7914980a 100644 --- a/src/ElementalTools/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -18,10 +18,14 @@ #define STBI_WINDOWS_UTF8 #endif +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic ignored "-Wmacro-redefined" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "stb_image_resize2.h" +#pragma clang diagnostic pop #include "bc7enc.cpp" diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 61f871df..1ef8d078 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -10,6 +10,9 @@ #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) #endif +#define Max(a,b) ((a) > (b) ? (a) : (b)) +#define Min(a,b) ((a) < (b) ? (a) : (b)) + #define PRINT_COLOR_BUFFER(buffer, totalWidth) \ { \ auto floatData = (float*)buffer.Items; \ diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index 484f350b..03b74ffa 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -442,8 +442,8 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) for (uint32_t i = 0; i < mipLevelCount; i++) { - auto currentWidth = max(1u, width >> i); - auto currentHeight = max(1u, height >> i); + auto currentWidth = Max(1u, width >> i); + auto currentHeight = Max(1u, height >> i); mipData[i] = (uint8_t*)malloc(currentWidth * currentHeight * 16); auto testColor = TestMipColors[i % ARRAYSIZE(TestMipColors)]; @@ -463,8 +463,8 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) // Act for (uint32_t i = 0; i < mipLevelCount; i++) { - auto currentWidth = max(1u, width >> i); - auto currentHeight = max(1u, height >> i); + auto currentWidth = Max(1u, width >> i); + auto currentHeight = Max(1u, height >> i); ElemCopyDataToGraphicsResourceParameters parameters = { @@ -487,8 +487,8 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) for (uint32_t i = 0; i < mipLevelCount; i++) { - auto currentWidth = max(1u, width >> i); - auto currentHeight = max(1u, height >> i); + auto currentWidth = Max(1u, width >> i); + auto currentHeight = Max(1u, height >> i); auto readbackBuffer = TestCreateGpuBuffer(graphicsDevice, currentWidth * currentHeight * sizeof(float), ElemGraphicsHeapType_Readback); uint32_t resourceIdList[] = { (uint32_t)texture.ReadDescriptor, (uint32_t)readbackBuffer.WriteDescriptor, i }; From 206e588e188583d740b99a1c03116e7117662626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:02:02 +0100 Subject: [PATCH 62/93] Fix meshletbuilder tests --- src/ElementalTools/Meshes/MeshletBuilder.cpp | 2 +- tests/ToolsTests/MeshBuilderTests.cpp | 24 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ElementalTools/Meshes/MeshletBuilder.cpp b/src/ElementalTools/Meshes/MeshletBuilder.cpp index c9680bcf..62c4e78f 100644 --- a/src/ElementalTools/Meshes/MeshletBuilder.cpp +++ b/src/ElementalTools/Meshes/MeshletBuilder.cpp @@ -36,7 +36,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf auto vertexCount = meshopt_generateVertexRemap(vertexRemap.Pointer, indexBufferPointer, indexCount, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize); auto vertexList = SystemPushArrayZero(MeshletBuilderMemoryArena, vertexCount * vertexBuffer.VertexSize); - auto indexList = SystemPushArrayZero(stackMemoryArena, indexCount); + auto indexList = SystemPushArrayZero(MeshletBuilderMemoryArena, indexCount); meshopt_remapVertexBuffer(vertexList.Pointer, vertexBuffer.Data.Items, indexCount, vertexBuffer.VertexSize, vertexRemap.Pointer); meshopt_remapIndexBuffer(indexList.Pointer, indexBufferPointer, indexCount, vertexRemap.Pointer); diff --git a/tests/ToolsTests/MeshBuilderTests.cpp b/tests/ToolsTests/MeshBuilderTests.cpp index b19e44ba..7c2123c5 100644 --- a/tests/ToolsTests/MeshBuilderTests.cpp +++ b/tests/ToolsTests/MeshBuilderTests.cpp @@ -60,10 +60,12 @@ UTEST(MeshBuilder, CheckVertexBuffer) { // Arrange const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; - uint32_t indexList[vertexCount / 3]; + uint32_t indexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount); - auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount); // Act auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); @@ -103,10 +105,12 @@ UTEST(MeshBuilder, CheckVertexBuffer_NoDuplicates) { // Arrange const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; - uint32_t indexList[vertexCount / 3]; + uint32_t indexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); - auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount - 3); for (uint32_t i = 0; i < 3; i++) { @@ -128,10 +132,12 @@ UTEST(MeshBuilder, CheckMeshletVertexIndexBuffer) { // Arrange const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; - uint32_t indexList[vertexCount / 3]; + uint32_t indexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); - auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount - 3); // Act auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); @@ -159,10 +165,12 @@ UTEST(MeshBuilder, CheckMeshletTriangleIndexBuffer) { // Arrange const uint32_t vertexCount = 210; + TestMeshVertex vertexList[vertexCount]; - uint32_t indexList[vertexCount / 3]; + uint32_t indexList[vertexCount]; + auto vertexBuffer = TestBuildVertexBuffer(vertexList, vertexCount - 3); - auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount / 3); + auto indexBuffer = TestBuildIndexBuffer(indexList, vertexCount - 3); // Act auto result = ElemBuildMeshlets(vertexBuffer, indexBuffer, NULL); From 591ec11f4ffc93d6a180052d44bf01b6367e598e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:25:47 +0100 Subject: [PATCH 63/93] Test --- .github/workflows/build.yml | 2 -- .github/workflows/run-tests.yml | 9 ++++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3cec024..107570a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,8 +27,6 @@ jobs: with: arch: ${{ inputs.architecture }} - # TODO: For the moment Github actions doesn't have the latest version of ubuntu - # we will re enable the linux build after # TODO: libwayland-dev wayland-protocols libdecor-devel - name: Setup Dependencies (Linux) if: inputs.platform == 'linux' diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 53031d30..d1d052ae 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -59,8 +59,7 @@ jobs: if: inputs.platform == 'osx' run: ./tests/ToolsTests.app/Contents/MacOS/ToolsTests - #- name: Run Graphics Tests - #if: inputs.platform == 'win' - #run: | -#$ErrorView = 'NormalView' -# ./Tests/GraphicsTests + - name: Run Graphics Tests (MacOS) + if: inputs.platform == 'osx' + run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicTests + From b08f9fbe96ac8fe01f879be2fd559bedb46df9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:26:33 +0100 Subject: [PATCH 64/93] Test --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index d1d052ae..65919985 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -61,5 +61,5 @@ jobs: - name: Run Graphics Tests (MacOS) if: inputs.platform == 'osx' - run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicTests + run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicTests From 6b099f456c3372bdac2348f1453f1002309ba7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:43:41 +0100 Subject: [PATCH 65/93] Fix --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 107570a7..88f337a2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,7 @@ jobs: if: inputs.platform == 'linux' run: | sudo apt-get update + sudo apt install libstdc++-dev - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master From a1451157dff4d2b1e63f7d81c5c4cf3066499efa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:45:39 +0100 Subject: [PATCH 66/93] Fix --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88f337a2..ec67aa91 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: if: inputs.platform == 'linux' run: | sudo apt-get update - sudo apt install libstdc++-dev + sudo apt install libstdc++-14-dev - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master From f4a36944964edeed92f6fdeb7c71de61598efa59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:48:17 +0100 Subject: [PATCH 67/93] Fix --- .github/workflows/build.yml | 1 - CMakeLists.txt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec67aa91..107570a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,6 @@ jobs: if: inputs.platform == 'linux' run: | sudo apt-get update - sudo apt install libstdc++-14-dev - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master diff --git a/CMakeLists.txt b/CMakeLists.txt index c33c7223..8df7d726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ if(WIN32) add_compile_options(/W4) elseif(LINUX) - set(CMAKE_CXX_COMPILER "clang++") - set(CMAKE_C_COMPILER "clang") + #set(CMAKE_CXX_COMPILER "clang++") + #set(CMAKE_C_COMPILER "clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lm") else() From fd6d36a6136317762ff4605315c4112abb7e094d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 10:58:11 +0100 Subject: [PATCH 68/93] Fix --- .github/workflows/build.yml | 5 +++-- CMakeLists.txt | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 107570a7..1423e69a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,8 +30,9 @@ jobs: # TODO: libwayland-dev wayland-protocols libdecor-devel - name: Setup Dependencies (Linux) if: inputs.platform == 'linux' - run: | - sudo apt-get update + uses: jhchundev/install-llvm-action@v1.0.0 + with: + version: "18.1.8" - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master diff --git a/CMakeLists.txt b/CMakeLists.txt index 8df7d726..c33c7223 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ if(WIN32) add_compile_options(/W4) elseif(LINUX) - #set(CMAKE_CXX_COMPILER "clang++") - #set(CMAKE_C_COMPILER "clang") + set(CMAKE_CXX_COMPILER "clang++") + set(CMAKE_C_COMPILER "clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lm") else() From 7d56310c67734e5e5c40cf87476b987e5b1efe45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:01:44 +0100 Subject: [PATCH 69/93] Test --- .github/workflows/build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1423e69a..e9f6090a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,13 +27,6 @@ jobs: with: arch: ${{ inputs.architecture }} - # TODO: libwayland-dev wayland-protocols libdecor-devel - - name: Setup Dependencies (Linux) - if: inputs.platform == 'linux' - uses: jhchundev/install-llvm-action@v1.0.0 - with: - version: "18.1.8" - - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master From bd9f040784954d1f61fa54e0afc7e3519f51fab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:07:33 +0100 Subject: [PATCH 70/93] Fix --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c33c7223..0e06f010 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ if(WIN32) elseif(LINUX) set(CMAKE_CXX_COMPILER "clang++") set(CMAKE_C_COMPILER "clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lm") else() if(BUILD_FOR_IOS) From e1ddff6909fa57bd0ea55ffc8756c7c6103b17c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:13:05 +0100 Subject: [PATCH 71/93] Fix --- .github/workflows/build.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e9f6090a..fab49805 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,13 @@ jobs: with: arch: ${{ inputs.architecture }} + # TODO: libwayland-dev wayland-protocols libdecor-devel + - name: Setup Dependencies (Linux) + if: inputs.platform == 'linux' + run: | + sudo apt-get update + sudo apt-get install libwayland-dev wayland-protocols libudev-dev libdecor-0-dev + - name: Setup Ninja uses: seanmiddleditch/gha-setup-ninja@master From 7ffaa13179f16ee950396bba08aff42d8e5d21e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:22:02 +0100 Subject: [PATCH 72/93] Fix --- external/CMakeLists.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index d7bdc4e5..0f031608 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -33,6 +33,19 @@ if(LINUX) set(GENERATED_DIR "${CMAKE_BINARY_DIR}/generated") file(MAKE_DIRECTORY ${GENERATED_DIR}) + add_custom_command( + OUTPUT + ${GENERATED_DIR}/xdg-shell-client-protocol.h + ${GENERATED_DIR}/xdg-shell-protocol.c + COMMAND wayland-scanner client-header + ${STABLE_PROTOCOLS_DIR}/xdg-shell/xdg-shell.xml + ${GENERATED_DIR}/xdg-shell-client-protocol.h + COMMAND wayland-scanner private-code + ${STABLE_PROTOCOLS_DIR}/xdg-shell/xdg-shell.xml + ${GENERATED_DIR}/xdg-shell-protocol.c + COMMENT "Generating xdg-shell protocol files" + ) + # Generate relative-pointer protocol files add_custom_command( OUTPUT ${GENERATED_DIR}/relative-pointer-unstable-v1-client-protocol.h ${GENERATED_DIR}/relative-pointer-unstable-v1-client-protocol.c From 827ee4a4eb0aecb63c947f9be7190d1c2e0b6a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:26:08 +0100 Subject: [PATCH 73/93] Fix --- external/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0f031608..eec265ee 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -70,6 +70,8 @@ if(LINUX) ${GENERATED_DIR}/relative-pointer-unstable-v1-client-protocol.c ${GENERATED_DIR}/pointer-constraints-unstable-v1-client-protocol.h ${GENERATED_DIR}/pointer-constraints-unstable-v1-client-protocol.c + ${GENERATED_DIR}/xdg-shell-client-protocol.h + ${GENERATED_DIR}/xdg-shell-protocol.c ) target_include_directories(Wayland INTERFACE ${GENERATED_DIR}) From f05bd0930894277445384e102089017e6753e60a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:29:04 +0100 Subject: [PATCH 74/93] Fix --- src/Elemental/Linux/UnityBuild.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Elemental/Linux/UnityBuild.cpp b/src/Elemental/Linux/UnityBuild.cpp index a47e62d1..d2443844 100644 --- a/src/Elemental/Linux/UnityBuild.cpp +++ b/src/Elemental/Linux/UnityBuild.cpp @@ -12,17 +12,22 @@ #include "Graphics/Vulkan/VulkanGraphicsDevice.cpp" #include "Graphics/Vulkan/VulkanCommandList.cpp" #include "Graphics/Vulkan/VulkanSwapChain.cpp" -#include "Graphics/Vulkan/VulkanTexture.cpp" +#include "Graphics/Vulkan/VulkanResource.cpp" +#include "Graphics/Vulkan/VulkanResourceBarrier.cpp" #include "Graphics/Vulkan/VulkanShader.cpp" #include "Graphics/Vulkan/VulkanRendering.cpp" #include "Graphics/ShaderReader.cpp" +#include "Graphics/ResourceDeleteQueue.cpp" #include "Graphics/CommandAllocatorPool.cpp" +#include "Graphics/ResourceBarrier.cpp" #include "Graphics/GraphicsDevice.cpp" #include "Graphics/CommandList.cpp" #include "Graphics/SwapChain.cpp" +#include "Graphics/Resource.cpp" #include "Graphics/Shader.cpp" #include "Graphics/Rendering.cpp" +#include "Graphics/UploadBufferPool.cpp" #include "Inputs/Inputs.cpp" #include "Inputs/HidDevices.cpp" From bd499286dfd2eb1b67f3f88c2b92a4665b58425e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:36:37 +0100 Subject: [PATCH 75/93] Fix --- src/ElementalTools/Platforms/Linux/PreCompiledHeader.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h index 21f58a65..8428b8bc 100644 --- a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include From 7f9b5aad60f9fad22bbb4650d277670e08d8bede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 11:57:55 +0100 Subject: [PATCH 76/93] Fix --- src/Elemental/ElementalLoader.c | 12 ++++++------ utilities/CodeGenerator/CLoaderCodeGenerator.cs | 13 ++++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index ffd42437..d321a331 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -54,11 +54,11 @@ typedef struct ElementalFunctions ElemSwapChainInfo (*ElemGetSwapChainInfo)(ElemSwapChain); void (*ElemSetSwapChainTiming)(ElemSwapChain, unsigned int, unsigned int); void (*ElemPresentSwapChain)(ElemSwapChain); - ElemGraphicsHeap (*ElemCreateGraphicsHeap)(ElemGraphicsDevice, unsigned long long, ElemGraphicsHeapOptions const *); + ElemGraphicsHeap (*ElemCreateGraphicsHeap)(ElemGraphicsDevice, uint64_t, ElemGraphicsHeapOptions const *); void (*ElemFreeGraphicsHeap)(ElemGraphicsHeap); ElemGraphicsResourceInfo (*ElemCreateGraphicsBufferResourceInfo)(ElemGraphicsDevice, unsigned int, ElemGraphicsResourceUsage, ElemGraphicsResourceInfoOptions const *); ElemGraphicsResourceInfo (*ElemCreateTexture2DResourceInfo)(ElemGraphicsDevice, unsigned int, unsigned int, unsigned int, ElemGraphicsFormat, ElemGraphicsResourceUsage, ElemGraphicsResourceInfoOptions const *); - ElemGraphicsResource (*ElemCreateGraphicsResource)(ElemGraphicsHeap, unsigned long long, ElemGraphicsResourceInfo const *); + ElemGraphicsResource (*ElemCreateGraphicsResource)(ElemGraphicsHeap, uint64_t, ElemGraphicsResourceInfo const *); void (*ElemFreeGraphicsResource)(ElemGraphicsResource, ElemFreeGraphicsResourceOptions const *); ElemGraphicsResourceInfo (*ElemGetGraphicsResourceInfo)(ElemGraphicsResource); void (*ElemUploadGraphicsBufferData)(ElemGraphicsResource, unsigned int, ElemDataSpan); @@ -169,11 +169,11 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemGetSwapChainInfo = (ElemSwapChainInfo (*)(ElemSwapChain))GetElementalFunctionPointer("ElemGetSwapChainInfo"); listElementalFunctions.ElemSetSwapChainTiming = (void (*)(ElemSwapChain, unsigned int, unsigned int))GetElementalFunctionPointer("ElemSetSwapChainTiming"); listElementalFunctions.ElemPresentSwapChain = (void (*)(ElemSwapChain))GetElementalFunctionPointer("ElemPresentSwapChain"); - listElementalFunctions.ElemCreateGraphicsHeap = (ElemGraphicsHeap (*)(ElemGraphicsDevice, unsigned long long, ElemGraphicsHeapOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsHeap"); + listElementalFunctions.ElemCreateGraphicsHeap = (ElemGraphicsHeap (*)(ElemGraphicsDevice, uint64_t, ElemGraphicsHeapOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsHeap"); listElementalFunctions.ElemFreeGraphicsHeap = (void (*)(ElemGraphicsHeap))GetElementalFunctionPointer("ElemFreeGraphicsHeap"); listElementalFunctions.ElemCreateGraphicsBufferResourceInfo = (ElemGraphicsResourceInfo (*)(ElemGraphicsDevice, unsigned int, ElemGraphicsResourceUsage, ElemGraphicsResourceInfoOptions const *))GetElementalFunctionPointer("ElemCreateGraphicsBufferResourceInfo"); listElementalFunctions.ElemCreateTexture2DResourceInfo = (ElemGraphicsResourceInfo (*)(ElemGraphicsDevice, unsigned int, unsigned int, unsigned int, ElemGraphicsFormat, ElemGraphicsResourceUsage, ElemGraphicsResourceInfoOptions const *))GetElementalFunctionPointer("ElemCreateTexture2DResourceInfo"); - listElementalFunctions.ElemCreateGraphicsResource = (ElemGraphicsResource (*)(ElemGraphicsHeap, unsigned long long, ElemGraphicsResourceInfo const *))GetElementalFunctionPointer("ElemCreateGraphicsResource"); + listElementalFunctions.ElemCreateGraphicsResource = (ElemGraphicsResource (*)(ElemGraphicsHeap, uint64_t, ElemGraphicsResourceInfo const *))GetElementalFunctionPointer("ElemCreateGraphicsResource"); listElementalFunctions.ElemFreeGraphicsResource = (void (*)(ElemGraphicsResource, ElemFreeGraphicsResourceOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResource"); listElementalFunctions.ElemGetGraphicsResourceInfo = (ElemGraphicsResourceInfo (*)(ElemGraphicsResource))GetElementalFunctionPointer("ElemGetGraphicsResourceInfo"); listElementalFunctions.ElemUploadGraphicsBufferData = (void (*)(ElemGraphicsResource, unsigned int, ElemDataSpan))GetElementalFunctionPointer("ElemUploadGraphicsBufferData"); @@ -1031,7 +1031,7 @@ static inline void ElemPresentSwapChain(ElemSwapChain swapChain) listElementalFunctions.ElemPresentSwapChain(swapChain); } -static inline ElemGraphicsHeap ElemCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, unsigned long long sizeInBytes, ElemGraphicsHeapOptions const * options) +static inline ElemGraphicsHeap ElemCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes, ElemGraphicsHeapOptions const * options) { if (!LoadElementalFunctionPointers()) { @@ -1141,7 +1141,7 @@ static inline ElemGraphicsResourceInfo ElemCreateTexture2DResourceInfo(ElemGraph return listElementalFunctions.ElemCreateTexture2DResourceInfo(graphicsDevice, width, height, mipLevels, format, usage, options); } -static inline ElemGraphicsResource ElemCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, unsigned long long graphicsHeapOffset, ElemGraphicsResourceInfo const * resourceInfo) +static inline ElemGraphicsResource ElemCreateGraphicsResource(ElemGraphicsHeap graphicsHeap, uint64_t graphicsHeapOffset, ElemGraphicsResourceInfo const * resourceInfo) { if (!LoadElementalFunctionPointers()) { diff --git a/utilities/CodeGenerator/CLoaderCodeGenerator.cs b/utilities/CodeGenerator/CLoaderCodeGenerator.cs index 0a739943..b4f9ad8d 100644 --- a/utilities/CodeGenerator/CLoaderCodeGenerator.cs +++ b/utilities/CodeGenerator/CLoaderCodeGenerator.cs @@ -274,9 +274,16 @@ public void GenerateCode(CppCompilation compilation, string source, string input parameterValuesBuilder.Append(", "); } - builder.Append($"{parameter.Type.GetDisplayName()} {parameter.Name}"); - functionsStringBuilder.Append($"{parameter.Type.GetDisplayName()}"); - functionsDeclarationStringBuilder.Append($"{parameter.Type.GetDisplayName()}"); + var parameterType = parameter.Type.GetDisplayName(); + + if (parameterType == "unsigned long long") + { + parameterType = "uint64_t"; + } + + builder.Append($"{parameterType} {parameter.Name}"); + functionsStringBuilder.Append($"{parameterType}"); + functionsDeclarationStringBuilder.Append($"{parameterType}"); parameterValuesBuilder.Append($"{parameter.Name}"); } } From 1668c250823ff27be243200bcc2a32665cefda05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 12:07:01 +0100 Subject: [PATCH 77/93] Fix --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e06f010..d8fcc6c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ elseif(LINUX) set(CMAKE_C_COMPILER "clang") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lm") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) else() if(BUILD_FOR_IOS) set(CMAKE_SYSTEM_NAME iOS) From 833123880afa8583480fd82d9bdb5ca2729059e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 12:57:14 +0100 Subject: [PATCH 78/93] Fix --- tests/GraphicsTests/GraphicsTests.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 1ef8d078..0cc66423 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -1,6 +1,7 @@ #pragma once #include "Elemental.h" +#include #include #define TESTLOG_LENGTH 4096 From bfe48f04f2c47fd929ad09f49cd2f7b5d1c88192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 13:02:04 +0100 Subject: [PATCH 79/93] Fix --- .github/workflows/run-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 65919985..633cc4cb 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -61,5 +61,5 @@ jobs: - name: Run Graphics Tests (MacOS) if: inputs.platform == 'osx' - run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicTests + run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicsTests From c2d3c6c7a6d9987cc5b43c64eb65dd4950f71471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 8 Jan 2025 15:31:29 +0100 Subject: [PATCH 80/93] Fix --- .github/workflows/build.yml | 1 - .github/workflows/run-tests.yml | 6 +++--- doc/macos.md | 3 +++ src/Elemental/Common/SystemMemory.cpp | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fab49805..dc66da26 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,6 @@ jobs: with: arch: ${{ inputs.architecture }} - # TODO: libwayland-dev wayland-protocols libdecor-devel - name: Setup Dependencies (Linux) if: inputs.platform == 'linux' run: | diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 633cc4cb..1a38079e 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -59,7 +59,7 @@ jobs: if: inputs.platform == 'osx' run: ./tests/ToolsTests.app/Contents/MacOS/ToolsTests - - name: Run Graphics Tests (MacOS) - if: inputs.platform == 'osx' - run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicsTests + #- name: Run Graphics Tests (MacOS) + #if: inputs.platform == 'osx' + #run: ./tests/GraphicsTests.app/Contents/MacOS/GraphicsTests diff --git a/doc/macos.md b/doc/macos.md index de94aee7..b5906620 100644 --- a/doc/macos.md +++ b/doc/macos.md @@ -2,3 +2,6 @@ xattr -l /path/to/application.app You'll probably find com.apple.quarantine listed. If so, you can get rid of it with xattr -dr com.apple.quarantine /path/to/application.app + +https://docs.avaloniaui.net/docs/deployment/macOS +https://github.com/Kong/insomnia/blob/c4dff179daa1895246edbbe335c58d559167f761/.github/workflows/release-build.yml#L169 diff --git a/src/Elemental/Common/SystemMemory.cpp b/src/Elemental/Common/SystemMemory.cpp index 7753cf3b..6276d076 100644 --- a/src/Elemental/Common/SystemMemory.cpp +++ b/src/Elemental/Common/SystemMemory.cpp @@ -470,7 +470,6 @@ void SystemPopMemory(MemoryArena memoryArena, size_t sizeInBytes) if (memoryArena.Storage == stackMemoryArenaStorage) { - pointer = storage->CurrentPointer; storage->CurrentPointer -= sizeInBytes; } else From 04f4a90e5f76161508232b782fe37672091fb1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Thu, 9 Jan 2025 10:20:54 +0100 Subject: [PATCH 81/93] Test clang-format --- .clang-format | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..0e27bbfb --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +Language: Cpp +UseTab: Never +IndentWidth: 4 +ColumnLimit: 200 +PointerAlignment: Left +BreakBeforeBraces: Allman +Cpp11BracedListStyle: false +InsertBraces: true + +IndentCaseLabels: true +AlignAfterOpenBracket: DontAlign +AlignArrayOfStructures: None +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +PenaltyBreakAssignment: 15 +PenaltyBreakBeforeFirstCallParameter: 15 +PenaltyBreakOpenParenthesis: 15 +BinPackArguments: false +BinPackParameters: false From 310beac02bf19bd7cb311a104598d8add8c7a5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 10 Jan 2025 10:40:22 +0100 Subject: [PATCH 82/93] Finish basic UploadBufferPool implementation --- .../Apple/Graphics/MetalResource.cpp | 48 ++++++++++++++ .../Common/Graphics/UploadBufferPool.cpp | 62 ++++++++++--------- .../Common/Graphics/UploadBufferPool.h | 12 ++-- src/Elemental/Common/SystemFunctions.cpp | 2 +- src/Elemental/Common/SystemFunctions.h | 4 +- src/Elemental/Common/SystemMemory.cpp | 2 +- .../Graphics/DirectX12CommandList.cpp | 9 ++- .../Microsoft/Graphics/DirectX12CommandList.h | 3 + .../Microsoft/Graphics/DirectX12Resource.cpp | 48 ++++++++++++-- 9 files changed, 144 insertions(+), 46 deletions(-) diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index ee24825f..b50c2fbf 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -560,6 +560,8 @@ UploadBufferMemory> GetMetalUploadBuffer(ElemGraphics uploadBuffer.PoolItem->Buffer.reset(); } + // TODO: Need to update the fence with the command list like in DirectX12 + uploadBuffer.PoolItem->Buffer = CreateMetalUploadBuffer(graphicsDeviceData->Device.get(), uploadBuffer.PoolItem->SizeInBytes); uploadBuffer.PoolItem->CpuPointer = (uint8_t*)uploadBuffer.PoolItem->Buffer->contents(); @@ -613,6 +615,23 @@ void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopy auto uploadBuffer = GetMetalUploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); + // TODO: Can we do better here? + auto uploadBufferAlreadyInCommandList = false; + + for (uint32_t i = 0; i < MAX_UPLOAD_BUFFERS; i++) + { + if (commandListData->UploadBufferPoolItems[i] == uploadBuffer.PoolItem) + { + uploadBufferAlreadyInCommandList = true; + break; + } + } + + if (!uploadBufferAlreadyInCommandList) + { + commandListData->UploadBufferPoolItems[commandListData->UploadBufferCount++] = uploadBuffer.PoolItem; + } + if (commandListData->CommandEncoderType != MetalCommandEncoderType_Copy) { ResetMetalCommandEncoder(commandList); @@ -728,6 +747,35 @@ void MetalFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descript void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + ProcessResourceDeleteQueue(); + SystemClearMemoryArena(metalReadBackMemoryArena); + + auto graphicsDeviceData = GetMetalGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + graphicsDeviceData->UploadBufferGeneration++; + + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + auto uploadBuffersToDelete = GetUploadBufferPoolItemsToDelete(stackMemoryArena, bufferPool, graphicsDeviceData->UploadBufferGeneration); + + for (uint32_t j = 0; j < uploadBuffersToDelete.Length; j++) + { + auto uploadBufferToDelete = uploadBuffersToDelete[j]; + + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to purge upload buffer: Size=%d", uploadBufferToDelete->SizeInBytes); + + uploadBufferToDelete->Buffer.reset(); + uploadBufferToDelete->CurrentOffset = 0; + uploadBufferToDelete->SizeInBytes = 0; + } + } + } } diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp index b835bbae..43115265 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.cpp +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -1,6 +1,8 @@ #include "UploadBufferPool.h" #include "SystemFunctions.h" +// TODO: Purge upload buffers after a certain amount of generations if they are not used + template UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t alignment, uint64_t sizeInBytes) { @@ -22,14 +24,9 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB if (uploadBufferPool->CurrentUploadBuffer == nullptr) { uploadBufferPool->CurrentUploadBuffer = &uploadBufferPool->UploadBuffers[uploadBufferPool->CurrentUploadBufferIndex]; + uploadBufferPool->CurrentUploadBufferIndex = (uploadBufferPool->CurrentUploadBufferIndex + 1) % MAX_UPLOAD_BUFFERS; auto currentUploadBuffer = uploadBufferPool->CurrentUploadBuffer; - uploadBufferPool->CurrentUploadBufferIndex = uploadBufferPool->CurrentUploadBufferIndex + 1 % MAX_UPLOAD_BUFFERS; - - // TODO: Split that into a NeedBufferCreation and NeedBufferDelete - currentUploadBuffer->IsResetNeeded = true; - currentUploadBuffer->CurrentOffset = 0u; - uint64_t uploadBufferSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512llu * 1024u * 1024u); if (uploadBufferSize == 0) @@ -44,23 +41,30 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB } uploadBufferPool->LastBufferSize = uploadBufferSize; - currentUploadBuffer->SizeInBytes = uploadBufferSize; + + if (currentUploadBuffer->SizeInBytes != uploadBufferSize) + { + currentUploadBuffer->SizeInBytes = uploadBufferSize; + currentUploadBuffer->IsResetNeeded = true; + } + + currentUploadBuffer->CurrentOffset = 0u; } - // TODO: Check alignment auto offset = uploadBufferPool->CurrentUploadBuffer->CurrentOffset; - uint64_t alignedOffset = (offset + (alignment - 1)) & ~(alignment - 1); // TODO: Use System Align power of 2 + auto alignedOffset = SystemAlign(offset, alignment); uint64_t newOffset = alignedOffset + sizeInBytes; + // TODO: Better handle that case if (newOffset > uploadBufferPool->CurrentUploadBuffer->SizeInBytes) { SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Not enough space after alignment"); return { nullptr, 0ull }; } - // Update buffer offset to reflect our allocation uploadBufferPool->CurrentUploadBuffer->CurrentOffset = newOffset; + uploadBufferPool->CurrentUploadBuffer->LastUsedGeneration = generation; return { @@ -69,27 +73,27 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB }; } -/* -template -CommandListPoolItem* GetCommandListPoolItem(CommandAllocatorPoolItem* commandAllocatorPoolItem) +template +void UpdateUploadBufferPoolItemFence(UploadBufferPoolItem* uploadBufferPoolItem, ElemFence fence) { - auto commandListPoolItem = &commandAllocatorPoolItem->CommandListPoolItems[commandAllocatorPoolItem->CurrentCommandListIndex]; - SystemAssert (!commandListPoolItem->IsInUse); - - commandAllocatorPoolItem->CurrentCommandListIndex = (commandAllocatorPoolItem->CurrentCommandListIndex + 1) % MAX_COMMANDLIST; - - commandListPoolItem->IsInUse = true; - return commandListPoolItem; + uploadBufferPoolItem->Fence = fence; } -template -void ReleaseCommandListPoolItem(CommandListPoolItem* commandListPoolItem) +template +Span*> GetUploadBufferPoolItemsToDelete(MemoryArena memoryArena, UploadBufferDevicePool* uploadBufferPool, uint64_t generation) { - commandListPoolItem->IsInUse = false; -} + auto result = SystemPushArray*>(memoryArena, MAX_UPLOAD_BUFFERS); + auto resultCount = 0u; -template -void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence) -{ - commandAllocatorPoolItem->Fence = fence; -}*/ + for (uint32_t i = 0; i < MAX_UPLOAD_BUFFERS; i++) + { + auto uploadBufferPoolItem = &uploadBufferPool->UploadBuffers[i]; + + if (uploadBufferPoolItem->SizeInBytes > 0 && (generation - uploadBufferPoolItem->LastUsedGeneration > 500)) + { + result[resultCount++] = uploadBufferPoolItem; + } + } + + return result.Slice(0, resultCount); +} diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.h b/src/Elemental/Common/Graphics/UploadBufferPool.h index ad438c08..6830dbe0 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.h +++ b/src/Elemental/Common/Graphics/UploadBufferPool.h @@ -1,6 +1,7 @@ #pragma once #include "Elemental.h" +#include "SystemMemory.h" #define MAX_UPLOAD_BUFFERS 10 @@ -13,6 +14,7 @@ struct UploadBufferPoolItem bool IsResetNeeded; uint64_t CurrentOffset; uint8_t* CpuPointer; + uint64_t LastUsedGeneration; }; template @@ -36,10 +38,8 @@ struct UploadBufferDevicePool template UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t alignment, uint64_t sizeInBytes); -/* -template -void ReleaseCommandListPoolItem(CommandListPoolItem* commandListPoolItem); +template +void UpdateUploadBufferPoolItemFence(UploadBufferPoolItem* uploadBufferPoolItem, ElemFence fence); -template -void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence); -*/ +template +Span*> GetUploadBufferPoolItemsToDelete(MemoryArena memoryArena, UploadBufferDevicePool* uploadBufferPool, uint64_t generation); diff --git a/src/Elemental/Common/SystemFunctions.cpp b/src/Elemental/Common/SystemFunctions.cpp index a4e961b2..d611abee 100644 --- a/src/Elemental/Common/SystemFunctions.cpp +++ b/src/Elemental/Common/SystemFunctions.cpp @@ -17,7 +17,7 @@ size_t SystemRoundUpToPowerOf2(size_t value) return result; } -size_t SystemAlignToPowerOf2(size_t offset, size_t alignment) +size_t SystemAlign(size_t offset, size_t alignment) { return (offset + alignment - 1) & ~(alignment - 1); } diff --git a/src/Elemental/Common/SystemFunctions.h b/src/Elemental/Common/SystemFunctions.h index 60016b45..a405a757 100644 --- a/src/Elemental/Common/SystemFunctions.h +++ b/src/Elemental/Common/SystemFunctions.h @@ -39,7 +39,7 @@ size_t SystemRoundUpToPowerOf2(size_t value); /** - * Aligns the given offset to the specified power of two alignment. + * Aligns the given offset to the specified alignment. * * This function adjusts the input offset to the nearest equal or higher multiple of the specified alignment. * The alignment value must be a power of 2. If the alignment is not a power of 2, the behavior of the function @@ -50,7 +50,7 @@ size_t SystemRoundUpToPowerOf2(size_t value); * @return The aligned offset. If the input alignment is not a power of 2, the behavior is undefined or it * may return the input offset, depending on implementation. */ -size_t SystemAlignToPowerOf2(size_t offset, size_t alignment); +size_t SystemAlign(size_t offset, size_t alignment); /** * Rounds the given double value to the nearest integer. diff --git a/src/Elemental/Common/SystemMemory.cpp b/src/Elemental/Common/SystemMemory.cpp index 6276d076..ee65c654 100644 --- a/src/Elemental/Common/SystemMemory.cpp +++ b/src/Elemental/Common/SystemMemory.cpp @@ -423,7 +423,7 @@ void SystemDecommitMemory(MemoryArena memoryArena, void* pointer, size_t sizeInB void* SystemPushMemory(MemoryArena memoryArena, size_t sizeInBytes, AllocationState state) { - sizeInBytes = SystemAlignToPowerOf2(sizeInBytes, MEMORYARENA_DEFAULT_ALIGNMENT); + sizeInBytes = SystemAlign(sizeInBytes, MEMORYARENA_DEFAULT_ALIGNMENT); auto workingMemoryArena = GetStackWorkingMemoryArena(memoryArena); auto storage = workingMemoryArena.Storage; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp index f40fc119..2e3e0ba0 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp @@ -244,7 +244,8 @@ ElemCommandList DirectX12GetCommandList(ElemCommandQueue commandQueue, const Ele .CommandAllocatorPoolItem = commandAllocatorPoolItem, .CommandListPoolItem = commandListPoolItem, .GraphicsDevice = commandQueueData->GraphicsDevice, - .ResourceBarrierPool = resourceBarrierPool + .ResourceBarrierPool = resourceBarrierPool, + .UploadBufferCount = 0 }); SystemAddDataPoolItemFull(directX12CommandListPool, handle, { @@ -325,6 +326,12 @@ ElemFence DirectX12ExecuteCommandLists(ElemCommandQueue commandQueue, ElemComman } UpdateCommandAllocatorPoolItemFence(commandListData->CommandAllocatorPoolItem, fence); + + for (uint32_t j = 0; j < commandListData->UploadBufferCount; j++) + { + UpdateUploadBufferPoolItemFence(commandListData->UploadBufferPoolItems[j], fence); + } + ReleaseCommandListPoolItem(commandListData->CommandListPoolItem); FreeResourceBarrierPool(commandListData->ResourceBarrierPool); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.h b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.h index adf05a97..7ad54d54 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.h @@ -4,6 +4,7 @@ #include "SystemSpan.h" #include "Graphics/CommandAllocatorPool.h" #include "Graphics/ResourceBarrier.h" +#include "Graphics/UploadBufferPool.h" enum DirectX12PipelineStateType { @@ -39,6 +40,8 @@ struct DirectX12CommandListData ElemGraphicsDevice GraphicsDevice; bool IsCommitted; ResourceBarrierPool ResourceBarrierPool; + UploadBufferPoolItem>* UploadBufferPoolItems[MAX_UPLOAD_BUFFERS]; + uint32_t UploadBufferCount; }; struct DirectX12CommandListDataFull diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 985e4440..9d7517fb 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -641,7 +641,6 @@ ElemDataSpan DirectX12DownloadGraphicsBufferData(ElemGraphicsResource resource, return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } -// TODO: We create an initial 256 MB buffer for now. This buffer will need to be resized if a big upload is asked. ComPtr CreateDirectX12UploadBuffer(ComPtr graphicsDevice, uint64_t sizeInBytes) { D3D12_RESOURCE_DESC bufferDescription = @@ -703,10 +702,10 @@ UploadBufferMemory> GetDirectX12UploadBuffer(ElemGraphics if (uploadBuffer.PoolItem->Fence.FenceValue > 0) { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Fence: %d", uploadBuffer.PoolItem->Fence.FenceValue); DirectX12WaitForFenceOnCpu(uploadBuffer.PoolItem->Fence); } - // TODO: We need another flag to know if we need to delete if (uploadBuffer.PoolItem->Buffer != nullptr) { SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to delete buffer"); @@ -727,7 +726,6 @@ UploadBufferMemory> GetDirectX12UploadBuffer(ElemGraphics void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) { // TODO: Implement optimizations on the copy queue. On windows, use DirectStorage - // TODO: Implement file source auto stackMemoryArena = SystemGetStackMemoryArena(); SystemAssert(commandList != ELEM_HANDLE_NULL); @@ -749,14 +747,12 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem ReadOnlySpan sourceData; - // TODO: File Source + // TODO: Implement file source if (parameters->SourceType == ElemCopyDataSourceType_Memory) { sourceData = ReadOnlySpan(parameters->SourceMemoryData.Items, parameters->SourceMemoryData.Length); } - // TODO: Unit test first !!!!! - auto uploadBufferAlignment = 4u; auto uploadBufferSizeInBytes = sourceData.Length; DirectX12GraphicsTextureMipCopyInfo textureMipCopyInfo = {}; @@ -777,6 +773,23 @@ void DirectX12CopyDataToGraphicsResource(ElemCommandList commandList, const Elem auto uploadBuffer = GetDirectX12UploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); + // TODO: Can we do better here? + auto uploadBufferAlreadyInCommandList = false; + + for (uint32_t i = 0; i < MAX_UPLOAD_BUFFERS; i++) + { + if (commandListData->UploadBufferPoolItems[i] == uploadBuffer.PoolItem) + { + uploadBufferAlreadyInCommandList = true; + break; + } + } + + if (!uploadBufferAlreadyInCommandList) + { + commandListData->UploadBufferPoolItems[commandListData->UploadBufferCount++] = uploadBuffer.PoolItem; + } + if (resourceData->Type == ElemGraphicsResourceType_Buffer) { memcpy(uploadBuffer.PoolItem->CpuPointer + uploadBuffer.Offset, sourceData.Pointer, sourceData.Length); @@ -962,6 +975,8 @@ void DirectX12FreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor desc void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + ProcessResourceDeleteQueue(); SystemClearMemoryArena(directX12ReadBackMemoryArena); @@ -969,4 +984,25 @@ void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevi SystemAssert(graphicsDeviceData); graphicsDeviceData->UploadBufferGeneration++; + + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + auto uploadBuffersToDelete = GetUploadBufferPoolItemsToDelete(stackMemoryArena, bufferPool, graphicsDeviceData->UploadBufferGeneration); + + for (uint32_t j = 0; j < uploadBuffersToDelete.Length; j++) + { + auto uploadBufferToDelete = uploadBuffersToDelete[j]; + + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to purge upload buffer: Size=%d", uploadBufferToDelete->SizeInBytes); + + uploadBufferToDelete->Buffer.Reset(); + uploadBufferToDelete->CurrentOffset = 0; + uploadBufferToDelete->SizeInBytes = 0; + } + } + } } From e41bb90f26afa06760cf06e6bdfc0161f4bb2e8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 11 Jan 2025 16:07:03 +0100 Subject: [PATCH 83/93] Sampler in DirectX12 --- .../99-Renderer/Data/RenderMesh.hlsl | 45 +++-- samples/Elemental/99-Renderer/main.c | 15 +- .../Apple/Graphics/MetalResource.cpp | 13 ++ src/Elemental/Apple/Graphics/MetalResource.h | 4 + src/Elemental/Common/Graphics/Resource.cpp | 15 ++ .../Common/Graphics/ResourceDeleteQueue.cpp | 7 +- .../Common/Graphics/ResourceDeleteQueue.h | 3 +- .../Common/Graphics/UploadBufferPool.cpp | 2 - .../Common/Graphics/Vulkan/VulkanResource.cpp | 14 ++ .../Common/Graphics/Vulkan/VulkanResource.h | 4 + src/Elemental/Elemental.h | 171 +++++++++++------- src/Elemental/ElementalLoader.c | 85 +++++++++ .../Graphics/DirectX12CommandList.cpp | 5 +- .../Microsoft/Graphics/DirectX12Config.h | 1 + .../Graphics/DirectX12GraphicsDevice.cpp | 35 +++- .../Graphics/DirectX12GraphicsDevice.h | 3 + .../Microsoft/Graphics/DirectX12Resource.cpp | 124 ++++++++++++- .../Microsoft/Graphics/DirectX12Resource.h | 4 + .../Microsoft/Graphics/DirectX12Shader.cpp | 30 --- src/ElementalTools/ElementalTools.h | 1 + .../SceneLoading/SceneLoaderObj.cpp | 15 +- tests/GraphicsTests/ResourceTests.cpp | 136 ++++++++++++++ 22 files changed, 603 insertions(+), 129 deletions(-) diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 1331d1b0..ca4a4782 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -11,8 +11,10 @@ struct ShaderParameters uint32_t MeshletTriangleIndexOffset; float Scale; float3 Translation; - uint32_t MaterialId; + uint32_t Reserved; float4 Rotation; + uint32_t MaterialId; + uint32_t TextureSampler; }; [[vk::push_constant]] @@ -49,10 +51,10 @@ struct Vertex struct VertexOutput { float4 Position: SV_Position; - float3 WorldNormal: NORMAL0; - float2 TextureCoordinates: TEXCOORD0; - uint MeshletIndex : COLOR0; - uint MaterialId : COLOR1; + float3 WorldNormal: Attribute0; + float2 TextureCoordinates: Attribute1; + nointerpolation uint MeshletIndex: Attribute2; + nointerpolation uint MaterialId: Attribute3; }; #define IDENTITY_MATRIX float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) @@ -66,7 +68,7 @@ float3 RotateQuaternion(float3 v, float4 q) [OutputTopology("triangle")] [NumThreads(126, 1, 1)] void MeshMain(in uint groupId: SV_GroupID, - in uint groupThreadId : SV_GroupThreadID, + in uint groupThreadId: SV_GroupThreadID, out vertices VertexOutput vertices[64], out indices uint3 indices[126]) { @@ -128,28 +130,25 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 ByteAddressBuffer materialBuffer = ResourceDescriptorHeap[parameters.MaterialBuffer]; ShaderMaterial material = materialBuffer.Load(parameters.MaterialId * sizeof(ShaderMaterial)); + SamplerState textureSampler = SamplerDescriptorHeap[parameters.TextureSampler]; + if (frameData.ShowMeshlets == 0) { if (material.AlbedoTextureId >= 0) { Texture2D albedoTexture = ResourceDescriptorHeap[material.AlbedoTextureId]; - - // TODO: This is a hack because we don't have a sampler for now - float width; - float height; - albedoTexture.GetDimensions(width, height); - - // Scale UV by texture size - float2 scaledUV = input.TextureCoordinates * float2(width, height); - - // Convert to integer. Use floor (or round) for nearest: - int coordX = ( (int)floor(scaledUV.x) % (int)width + (int)width ) % (int)width; - int coordY = ( (int)floor(scaledUV.y) % (int)height + (int)height ) % (int)height; - - // If your texture is oriented with y=0 at the top, you might flip here: - coordY = ((int)height - 1 - coordY); - - return albedoTexture.Load(int3(coordX, coordY, 0)); + float4 albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates); + + // TODO: Doing discard on the main pass is really bad for performance. + // Doing it disable the early depth test in the shader, so all pixel shader code has to run for + // occluded pixels. + // TODO: We will need to process transparent objects in another path in another shader + if (albedo.a < 0.5) + { + discard; + } + + return albedo; } return float4(0, 0, 1, 1);; diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index fcf40367..a1a87b95 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -20,8 +20,10 @@ typedef struct uint32_t MeshletTriangleIndexOffset; float Scale; ElemVector3 Translation; - uint32_t MaterialId; + uint32_t Reserved; ElemVector4 Rotation; + uint32_t MaterialId; + uint32_t TextureSampler; } ShaderParameters; typedef struct @@ -114,6 +116,16 @@ void InitSample(void* payload) CreateDepthBuffer(applicationPayload, swapChainInfo.Width, swapChainInfo.Height); SampleLoadScene(applicationPayload->ScenePath, &applicationPayload->TestSceneData, &applicationPayload->GpuMemory); + ElemGraphicsSamplerInfo samplerInfo = + { + .MinFilter = ElemGraphicsSamplerFilter_Linear, + .MagFilter = ElemGraphicsSamplerFilter_Linear, + .MipFilter = ElemGraphicsSamplerFilter_Linear, + .MaxAnisotropy = 16, + }; + + applicationPayload->ShaderParameters.TextureSampler = ElemCreateGraphicsSampler(applicationPayload->GraphicsDevice, &samplerInfo); + ElemDataSpan shaderData = SampleReadFile(!applicationPayload->AppSettings.PreferVulkan ? "RenderMesh.shader": "RenderMesh_vulkan.shader", true); ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(applicationPayload->GraphicsDevice, shaderData); @@ -122,6 +134,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", + .CullMode = ElemGraphicsCullMode_None, // TODO: We need to deactivate cull only for transparent objects! .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, .DepthStencil = { diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index b50c2fbf..e3ed30d6 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -779,3 +779,16 @@ void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) } } +ElemGraphicsSampler MetalCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) +{ + return {}; +} + +ElemGraphicsSamplerInfo MetalGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) +{ + return {}; +} + +void MetalFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) +{ +} diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index d44868b5..21b6b336 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -62,3 +62,7 @@ void MetalFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descript void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); ElemGraphicsResource CreateMetalGraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap graphicsHeap, ElemGraphicsResourceUsage usage, NS::SharedPtr resource, bool isPresentTexture); + +ElemGraphicsSampler MetalCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo); +ElemGraphicsSamplerInfo MetalGetGraphicsSamplerInfo(ElemGraphicsSampler sampler); +void MetalFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options); diff --git a/src/Elemental/Common/Graphics/Resource.cpp b/src/Elemental/Common/Graphics/Resource.cpp index 4e89664f..e99f4c8d 100644 --- a/src/Elemental/Common/Graphics/Resource.cpp +++ b/src/Elemental/Common/Graphics/Resource.cpp @@ -85,3 +85,18 @@ ElemAPI void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsD { DispatchGraphicsFunction(ProcessGraphicsResourceDeleteQueue, graphicsDevice); } + +ElemAPI ElemGraphicsSampler ElemCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) +{ + DispatchReturnGraphicsFunction(CreateGraphicsSampler, graphicsDevice, samplerInfo); +} + +ElemAPI ElemGraphicsSamplerInfo ElemGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) +{ + DispatchReturnGraphicsFunction(GetGraphicsSamplerInfo, sampler); +} + +ElemAPI void ElemFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) +{ + DispatchGraphicsFunction(FreeGraphicsSampler, sampler, options); +} diff --git a/src/Elemental/Common/Graphics/ResourceDeleteQueue.cpp b/src/Elemental/Common/Graphics/ResourceDeleteQueue.cpp index 1d865c05..93982bd2 100644 --- a/src/Elemental/Common/Graphics/ResourceDeleteQueue.cpp +++ b/src/Elemental/Common/Graphics/ResourceDeleteQueue.cpp @@ -57,7 +57,8 @@ void ProcessResourceDeleteQueue() auto entry = &GraphicsResourceDeleteQueue[i]; if ((entry->Type == ResourceDeleteType_Resource && entry->Resource != ELEM_HANDLE_NULL) || - (entry->Type == ResourceDeleteType_Descriptor && (int)entry->Resource != -1)) + (entry->Type == ResourceDeleteType_Descriptor && (int)entry->Resource != -1) || + (entry->Type == ResourceDeleteType_Sampler && (int)entry->Resource != -1)) { auto fencesCompleted = true; @@ -80,6 +81,10 @@ void ProcessResourceDeleteQueue() { ElemFreeGraphicsResourceDescriptor(entry->Resource, nullptr); } + else if (entry->Type == ResourceDeleteType_Sampler) + { + ElemFreeGraphicsSampler(entry->Resource, nullptr); + } entry->Resource = ELEM_HANDLE_NULL; entry->NextEntry = GraphicsResourceDeleteQueueFreeListIndex; diff --git a/src/Elemental/Common/Graphics/ResourceDeleteQueue.h b/src/Elemental/Common/Graphics/ResourceDeleteQueue.h index f19b0eed..d93cac36 100644 --- a/src/Elemental/Common/Graphics/ResourceDeleteQueue.h +++ b/src/Elemental/Common/Graphics/ResourceDeleteQueue.h @@ -6,7 +6,8 @@ enum ResourceDeleteType { ResourceDeleteType_Resource, - ResourceDeleteType_Descriptor + ResourceDeleteType_Descriptor, + ResourceDeleteType_Sampler }; void EnqueueResourceDeleteEntry(MemoryArena memoryArena, ElemHandle resource, ResourceDeleteType type, ElemFenceSpan fences); diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp index 43115265..7cc7716a 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.cpp +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -1,8 +1,6 @@ #include "UploadBufferPool.h" #include "SystemFunctions.h" -// TODO: Purge upload buffers after a certain amount of generations if they are not used - template UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadBufferPool, uint64_t generation, uint64_t alignment, uint64_t sizeInBytes) { diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 87ebf1d0..7130abc9 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -742,3 +742,17 @@ void VulkanProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) ProcessResourceDeleteQueue(); SystemClearMemoryArena(vulkanReadBackMemoryArena); } + +ElemGraphicsSampler VulkanCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) +{ + return {}; +} + +ElemGraphicsSamplerInfo VulkanGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) +{ + return {}; +} + +void VulkanFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) +{ +} diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index dba23cdc..bc26c150 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -64,3 +64,7 @@ void VulkanFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descrip void VulkanProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); void VulkanGraphicsResourceBarrier(ElemCommandList commandList, ElemGraphicsResourceDescriptor descriptor, const ElemGraphicsResourceBarrierOptions* options); + +ElemGraphicsSampler VulkanCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo); +ElemGraphicsSamplerInfo VulkanGetGraphicsSamplerInfo(ElemGraphicsSampler sampler); +void VulkanFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 13161b68..9f301b74 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -327,6 +327,11 @@ typedef ElemHandle ElemGraphicsResource; */ typedef int32_t ElemGraphicsResourceDescriptor; +/** + * Handle that represents a graphics resource descriptor. + */ +typedef int32_t ElemGraphicsSampler; + /** * Handle that represents a shader library. */ @@ -415,6 +420,21 @@ typedef enum ElemGraphicsResourceDescriptorUsage_Write = 0x01 } ElemGraphicsResourceDescriptorUsage; +typedef enum +{ + ElemGraphicsSamplerFilter_Nearest = 0, + ElemGraphicsSamplerFilter_Linear = 1 +} ElemGraphicsSamplerFilter; + +typedef enum +{ + ElemGraphicsSamplerAddressMode_Repeat = 0, + ElemGraphicsSamplerAddressMode_RepeatMirror = 1, + ElemGraphicsSamplerAddressMode_ClampToEdge = 2, + ElemGraphicsSamplerAddressMode_ClampToEdgeMirror = 3, + ElemGraphicsSamplerAddressMode_ClampToBorderColor = 4 +} ElemGraphicsSamplerAddressMode; + typedef enum { ElemGraphicsFillMode_Solid = 0, @@ -514,6 +534,69 @@ typedef enum ElemRenderPassStoreAction_Discard = 1 } ElemRenderPassStoreAction; +/** + * Represents RGBA color. + */ +typedef struct +{ + // Red component. + float Red; + // Green component. + float Green; + // Blue component. + float Blue; + // Alpha component. + float Alpha; +} ElemColor; + +typedef struct +{ + // X coordinate of the rectangle's top left corner. + float X; + // Y coordinate of the rectangle's top left corner. + float Y; + // Width of the rectangle. + float Width; + // Height of the rectangle. + float Height; +} ElemRectangle; + +/** + * Represents a collection of rectangles. + */ +typedef struct +{ + // Pointer to an array of ElemRectangle. + ElemRectangle* Items; + // Number of items in the array. + uint32_t Length; +} ElemRectangleSpan; + +typedef struct +{ + float X, Y, Z; +} ElemVector3; + +typedef union +{ + // TODO: Fix compilation warning + struct + { + float X, Y, Z, W; + }; + + struct + { + ElemVector3 XYZ; + }; +} ElemVector4; + +typedef struct +{ + ElemVector3 MinPoint; + ElemVector3 MaxPoint; +} ElemBoundingBox; + /** * Configuration options for graphics initialization. */ @@ -737,6 +820,28 @@ typedef struct ElemFenceSpan FencesToWait; } ElemFreeGraphicsResourceDescriptorOptions; +typedef struct +{ + ElemGraphicsSamplerFilter MinFilter; + ElemGraphicsSamplerFilter MagFilter; + ElemGraphicsSamplerFilter MipFilter; + ElemGraphicsSamplerAddressMode AddressU; + ElemGraphicsSamplerAddressMode AddressV; + ElemGraphicsSamplerAddressMode AddressW; + uint32_t MaxAnisotropy; + ElemGraphicsCompareFunction CompareFunction; + ElemColor BorderColor; + float MinLod; + float MaxLod; + // TODO: Minimum/Maximum filters? +} ElemGraphicsSamplerInfo; + +typedef struct +{ + // Fences that the execution should wait on before starting. + ElemFenceSpan FencesToWait; +} ElemFreeGraphicsSamplerOptions; + typedef struct { uint32_t Offset; @@ -833,44 +938,6 @@ typedef struct ElemGraphicsResourceBarrierLayoutType AfterLayout; } ElemGraphicsResourceBarrierOptions; -/** - * Represents RGBA color. - */ -typedef struct -{ - // Red component. - float Red; - // Green component. - float Green; - // Blue component. - float Blue; - // Alpha component. - float Alpha; -} ElemColor; - -typedef struct -{ - // X coordinate of the rectangle's top left corner. - float X; - // Y coordinate of the rectangle's top left corner. - float Y; - // Width of the rectangle. - float Width; - // Height of the rectangle. - float Height; -} ElemRectangle; - -/** - * Represents a collection of rectangles. - */ -typedef struct -{ - // Pointer to an array of ElemRectangle. - ElemRectangle* Items; - // Number of items in the array. - uint32_t Length; -} ElemRectangleSpan; - /** * Defines a viewport for rendering. */ @@ -901,30 +968,6 @@ typedef struct uint32_t Length; } ElemViewportSpan; -typedef struct -{ - float X, Y, Z; -} ElemVector3; - -typedef union -{ - // TODO: Fix compilation warning - struct - { - float X, Y, Z, W; - }; - - struct - { - ElemVector3 XYZ; - }; -} ElemVector4; - -typedef struct -{ - ElemVector3 MinPoint; - ElemVector3 MaxPoint; -} ElemBoundingBox; /** * Configuration for a render pass target. */ @@ -1132,6 +1175,10 @@ ElemAPI void ElemFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor d ElemAPI void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); +ElemAPI ElemGraphicsSampler ElemCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo); +ElemAPI ElemGraphicsSamplerInfo ElemGetGraphicsSamplerInfo(ElemGraphicsSampler sampler); +ElemAPI void ElemFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options); + /** * Creates a shader library from provided binary data, allowing shaders to be loaded and used by graphics pipeline states. * @param graphicsDevice The device on which to create the shader library. diff --git a/src/Elemental/ElementalLoader.c b/src/Elemental/ElementalLoader.c index d321a331..f16e6e98 100644 --- a/src/Elemental/ElementalLoader.c +++ b/src/Elemental/ElementalLoader.c @@ -68,6 +68,9 @@ typedef struct ElementalFunctions ElemGraphicsResourceDescriptorInfo (*ElemGetGraphicsResourceDescriptorInfo)(ElemGraphicsResourceDescriptor); void (*ElemFreeGraphicsResourceDescriptor)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *); void (*ElemProcessGraphicsResourceDeleteQueue)(ElemGraphicsDevice); + ElemGraphicsSampler (*ElemCreateGraphicsSampler)(ElemGraphicsDevice, ElemGraphicsSamplerInfo const *); + ElemGraphicsSamplerInfo (*ElemGetGraphicsSamplerInfo)(ElemGraphicsSampler); + void (*ElemFreeGraphicsSampler)(ElemGraphicsSampler, ElemFreeGraphicsSamplerOptions const *); ElemShaderLibrary (*ElemCreateShaderLibrary)(ElemGraphicsDevice, ElemDataSpan); void (*ElemFreeShaderLibrary)(ElemShaderLibrary); ElemPipelineState (*ElemCompileGraphicsPipelineState)(ElemGraphicsDevice, ElemGraphicsPipelineStateParameters const *); @@ -183,6 +186,9 @@ static bool LoadElementalFunctionPointers(void) listElementalFunctions.ElemGetGraphicsResourceDescriptorInfo = (ElemGraphicsResourceDescriptorInfo (*)(ElemGraphicsResourceDescriptor))GetElementalFunctionPointer("ElemGetGraphicsResourceDescriptorInfo"); listElementalFunctions.ElemFreeGraphicsResourceDescriptor = (void (*)(ElemGraphicsResourceDescriptor, ElemFreeGraphicsResourceDescriptorOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsResourceDescriptor"); listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue = (void (*)(ElemGraphicsDevice))GetElementalFunctionPointer("ElemProcessGraphicsResourceDeleteQueue"); + listElementalFunctions.ElemCreateGraphicsSampler = (ElemGraphicsSampler (*)(ElemGraphicsDevice, ElemGraphicsSamplerInfo const *))GetElementalFunctionPointer("ElemCreateGraphicsSampler"); + listElementalFunctions.ElemGetGraphicsSamplerInfo = (ElemGraphicsSamplerInfo (*)(ElemGraphicsSampler))GetElementalFunctionPointer("ElemGetGraphicsSamplerInfo"); + listElementalFunctions.ElemFreeGraphicsSampler = (void (*)(ElemGraphicsSampler, ElemFreeGraphicsSamplerOptions const *))GetElementalFunctionPointer("ElemFreeGraphicsSampler"); listElementalFunctions.ElemCreateShaderLibrary = (ElemShaderLibrary (*)(ElemGraphicsDevice, ElemDataSpan))GetElementalFunctionPointer("ElemCreateShaderLibrary"); listElementalFunctions.ElemFreeShaderLibrary = (void (*)(ElemShaderLibrary))GetElementalFunctionPointer("ElemFreeShaderLibrary"); listElementalFunctions.ElemCompileGraphicsPipelineState = (ElemPipelineState (*)(ElemGraphicsDevice, ElemGraphicsPipelineStateParameters const *))GetElementalFunctionPointer("ElemCompileGraphicsPipelineState"); @@ -1381,6 +1387,85 @@ static inline void ElemProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice gra listElementalFunctions.ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); } +static inline ElemGraphicsSampler ElemCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, ElemGraphicsSamplerInfo const * samplerInfo) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + + #ifdef __cplusplus + ElemGraphicsSampler result = {}; + #else + ElemGraphicsSampler result = (ElemGraphicsSampler){0}; + #endif + + return result; + } + + if (!listElementalFunctions.ElemCreateGraphicsSampler) + { + assert(listElementalFunctions.ElemCreateGraphicsSampler); + + #ifdef __cplusplus + ElemGraphicsSampler result = {}; + #else + ElemGraphicsSampler result = (ElemGraphicsSampler){0}; + #endif + + return result; + } + + return listElementalFunctions.ElemCreateGraphicsSampler(graphicsDevice, samplerInfo); +} + +static inline ElemGraphicsSamplerInfo ElemGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + + #ifdef __cplusplus + ElemGraphicsSamplerInfo result = {}; + #else + ElemGraphicsSamplerInfo result = (ElemGraphicsSamplerInfo){0}; + #endif + + return result; + } + + if (!listElementalFunctions.ElemGetGraphicsSamplerInfo) + { + assert(listElementalFunctions.ElemGetGraphicsSamplerInfo); + + #ifdef __cplusplus + ElemGraphicsSamplerInfo result = {}; + #else + ElemGraphicsSamplerInfo result = (ElemGraphicsSamplerInfo){0}; + #endif + + return result; + } + + return listElementalFunctions.ElemGetGraphicsSamplerInfo(sampler); +} + +static inline void ElemFreeGraphicsSampler(ElemGraphicsSampler sampler, ElemFreeGraphicsSamplerOptions const * options) +{ + if (!LoadElementalFunctionPointers()) + { + assert(libraryElemental); + return; + } + + if (!listElementalFunctions.ElemFreeGraphicsSampler) + { + assert(listElementalFunctions.ElemFreeGraphicsSampler); + return; + } + + listElementalFunctions.ElemFreeGraphicsSampler(sampler, options); +} + static inline ElemShaderLibrary ElemCreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) { if (!LoadElementalFunctionPointers()) diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp index 2e3e0ba0..9a47d8c6 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp @@ -228,10 +228,11 @@ ElemCommandList DirectX12GetCommandList(ElemCommandQueue commandQueue, const Ele AssertIfFailedReturnNullHandle(commandListPoolItem->CommandList->Reset(commandAllocatorPoolItem->CommandAllocator, nullptr)); - auto descriptorHeaps = SystemPushArray(stackMemoryArena, 1); + auto descriptorHeaps = SystemPushArray(stackMemoryArena, 2); descriptorHeaps[0] = graphicsDeviceData->ResourceDescriptorHeap.Storage->DescriptorHeap.Get(); + descriptorHeaps[1] = graphicsDeviceData->SamplerDescriptorHeap.Storage->DescriptorHeap.Get(); - commandListPoolItem->CommandList->SetDescriptorHeaps(1, descriptorHeaps.Pointer); + commandListPoolItem->CommandList->SetDescriptorHeaps(2, descriptorHeaps.Pointer); // TODO: Can we set the root signature for all types all the time? commandListPoolItem->CommandList->SetGraphicsRootSignature(graphicsDeviceData->RootSignature.Get()); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Config.h b/src/Elemental/Microsoft/Graphics/DirectX12Config.h index 51d8e1a9..0c1b2955 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Config.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Config.h @@ -14,6 +14,7 @@ #define DIRECTX12_MAX_GRAPHICSHEAP 32 #define DIRECTX12_MAX_RESOURCES 1000000 +#define DIRECTX12_MAX_SAMPLERS 2048 #define DIRECTX12_MAX_MIPS 16 #define DIRECTX12_MAX_SWAPCHAIN_BUFFERS 3 diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp index 231cd9f7..956f9c51 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.cpp @@ -182,6 +182,36 @@ bool DirectX12CheckGraphicsDeviceCompatibility(ComPtr graphicsDe return false; } +D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return D3D12_COMPARISON_FUNC_NEVER; + + case ElemGraphicsCompareFunction_Less: + return D3D12_COMPARISON_FUNC_LESS; + + case ElemGraphicsCompareFunction_Equal: + return D3D12_COMPARISON_FUNC_EQUAL; + + case ElemGraphicsCompareFunction_LessEqual: + return D3D12_COMPARISON_FUNC_LESS_EQUAL; + + case ElemGraphicsCompareFunction_Greater: + return D3D12_COMPARISON_FUNC_GREATER; + + case ElemGraphicsCompareFunction_NotEqual: + return D3D12_COMPARISON_FUNC_NOT_EQUAL; + + case ElemGraphicsCompareFunction_GreaterEqual: + return D3D12_COMPARISON_FUNC_GREATER_EQUAL; + + case ElemGraphicsCompareFunction_Always: + return D3D12_COMPARISON_FUNC_ALWAYS; + } +} + DirectX12DescriptorHeap CreateDirectX12DescriptorHeap(ComPtr graphicsDevice, MemoryArena memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags, uint32_t length) { D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = @@ -285,7 +315,7 @@ ComPtr CreateDirectX12RootSignature(ComPtr rootSignatureDesc.Desc_1_1.pParameters = rootParameters; rootSignatureDesc.Desc_1_1.NumStaticSamplers = 0; rootSignatureDesc.Desc_1_1.pStaticSamplers = nullptr; - rootSignatureDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED; + rootSignatureDesc.Desc_1_1.Flags = D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED; ComPtr serializedRootSignature; AssertIfFailed(D3D12SerializeVersionedRootSignature(&rootSignatureDesc, serializedRootSignature.GetAddressOf(), nullptr)); @@ -403,6 +433,7 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions auto memoryArena = SystemAllocateMemoryArena(); auto resourceDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, DIRECTX12_MAX_RESOURCES); + auto samplerDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, DIRECTX12_MAX_SAMPLERS); auto rtvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); auto dsvDescriptorHeap = CreateDirectX12DescriptorHeap(device, memoryArena, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, DIRECTX12_MAX_RTVS); auto rootSignature = CreateDirectX12RootSignature(device); @@ -414,6 +445,7 @@ ElemGraphicsDevice DirectX12CreateGraphicsDevice(const ElemGraphicsDeviceOptions .Device = device, .RootSignature = rootSignature, .ResourceDescriptorHeap = resourceDescriptorHeap, + .SamplerDescriptorHeap = samplerDescriptorHeap, .RTVDescriptorHeap = rtvDescriptorHeap, .DSVDescriptorHeap = dsvDescriptorHeap, .MemoryArena = memoryArena, @@ -461,6 +493,7 @@ void DirectX12FreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) } FreeDirectX12DescriptorHeap(graphicsDeviceData->ResourceDescriptorHeap); + FreeDirectX12DescriptorHeap(graphicsDeviceData->SamplerDescriptorHeap); FreeDirectX12DescriptorHeap(graphicsDeviceData->RTVDescriptorHeap); FreeDirectX12DescriptorHeap(graphicsDeviceData->DSVDescriptorHeap); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h index 586f177d..f1deac26 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12GraphicsDevice.h @@ -18,6 +18,7 @@ struct DirectX12GraphicsDeviceData uint64_t CommandAllocationGeneration; uint64_t UploadBufferGeneration; DirectX12DescriptorHeap ResourceDescriptorHeap; + DirectX12DescriptorHeap SamplerDescriptorHeap; DirectX12DescriptorHeap RTVDescriptorHeap; DirectX12DescriptorHeap DSVDescriptorHeap; MemoryArena MemoryArena; @@ -41,6 +42,8 @@ extern ComPtr DxgiInfoQueue; DirectX12GraphicsDeviceData* GetDirectX12GraphicsDeviceData(ElemGraphicsDevice graphicsDevice); DirectX12GraphicsDeviceDataFull* GetDirectX12GraphicsDeviceDataFull(ElemGraphicsDevice graphicsDevice); +D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunction compareFunction); + D3D12_CPU_DESCRIPTOR_HANDLE CreateDirectX12DescriptorHandle(DirectX12DescriptorHeap descriptorHeap); void FreeDirectX12DescriptorHandle(DirectX12DescriptorHeap descriptorHeap, D3D12_CPU_DESCRIPTOR_HANDLE handle); uint32_t ConvertDirectX12DescriptorHandleToIndex(DirectX12DescriptorHeap descriptorHeap, D3D12_CPU_DESCRIPTOR_HANDLE handle); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 9d7517fb..038dc010 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -16,6 +16,7 @@ thread_local UploadBufferDevicePool> threadDirectX12Uploa // TODO: This descriptor infos should be linked to the graphics device like the resource desc heaps Span directX12ResourceDescriptorInfos; +Span directX12SamplerInfos; MemoryArena directX12ReadBackMemoryArena; void InitDirectX12ResourceMemory() @@ -25,9 +26,8 @@ void InitDirectX12ResourceMemory() directX12GraphicsHeapPool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_GRAPHICSHEAP); directX12GraphicsResourcePool = SystemCreateDataPool(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES); - // TODO: That push array cause a massive memory increase - // TODO: We can push the array with reserved memory but we need to be carreful to commit the memory directX12ResourceDescriptorInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_RESOURCES, AllocationState_Reserved); + directX12SamplerInfos = SystemPushArray(DirectX12MemoryArena, DIRECTX12_MAX_SAMPLERS, AllocationState_Reserved); // TODO: Allow to increase the size as a parameter directX12ReadBackMemoryArena = SystemAllocateMemoryArena(DIRECTX12_READBACK_MEMORY_ARENA); @@ -84,6 +84,39 @@ DXGI_FORMAT ConvertDirectX12FormatWithoutSrgbIfNeeded(DXGI_FORMAT format) } } +D3D12_FILTER_TYPE ConvertToDirectX12FilerType(ElemGraphicsSamplerFilter filter) +{ + switch (filter) + { + case ElemGraphicsSamplerFilter_Nearest: + return D3D12_FILTER_TYPE_POINT; + + case ElemGraphicsSamplerFilter_Linear: + return D3D12_FILTER_TYPE_LINEAR; + } +} + +D3D12_TEXTURE_ADDRESS_MODE ConvertToDirectX12TextureAddressMode(ElemGraphicsSamplerAddressMode addressMode) +{ + switch (addressMode) + { + case ElemGraphicsSamplerAddressMode_Repeat: + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + + case ElemGraphicsSamplerAddressMode_RepeatMirror: + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; + + case ElemGraphicsSamplerAddressMode_ClampToEdge: + return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + + case ElemGraphicsSamplerAddressMode_ClampToEdgeMirror: + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE; + + case ElemGraphicsSamplerAddressMode_ClampToBorderColor: + return D3D12_TEXTURE_ADDRESS_MODE_BORDER; + } +} + ElemGraphicsResource CreateDirectX12GraphicsResourceFromResource(ElemGraphicsDevice graphicsDevice, ElemGraphicsResourceType type, ElemGraphicsHeap heap, ComPtr resource, bool isPresentTexture) { InitDirectX12ResourceMemory(); @@ -1006,3 +1039,90 @@ void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevi } } } + +ElemGraphicsSampler DirectX12CreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) +{ + InitDirectX12ResourceMemory(); + + auto graphicsDeviceData = GetDirectX12GraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto descriptorHeap = graphicsDeviceData->SamplerDescriptorHeap; + auto descriptorHandle = CreateDirectX12DescriptorHandle(descriptorHeap); + + auto localSamplerInfo = *samplerInfo; + + if (localSamplerInfo.MaxAnisotropy == 0) + { + localSamplerInfo.MaxAnisotropy = 1; + } + + auto minFilter = ConvertToDirectX12FilerType(localSamplerInfo.MinFilter); + auto magFilter = ConvertToDirectX12FilerType(localSamplerInfo.MagFilter); + auto mipFilter = ConvertToDirectX12FilerType(localSamplerInfo.MipFilter); + auto reduction = localSamplerInfo.CompareFunction == ElemGraphicsCompareFunction_Never ? D3D12_FILTER_REDUCTION_TYPE_STANDARD : D3D12_FILTER_REDUCTION_TYPE_COMPARISON; + auto borderColor = localSamplerInfo.BorderColor; + auto filter = D3D12_ENCODE_BASIC_FILTER(minFilter, magFilter, mipFilter, reduction); + + if (localSamplerInfo.MaxAnisotropy > 1) + { + filter = (D3D12_FILTER)(D3D12_ANISOTROPIC_FILTERING_BIT | filter); + } + + D3D12_SAMPLER_DESC samplerDesc = + { + .Filter = filter, + .AddressU = ConvertToDirectX12TextureAddressMode(localSamplerInfo.AddressU), + .AddressV = ConvertToDirectX12TextureAddressMode(localSamplerInfo.AddressV), + .AddressW = ConvertToDirectX12TextureAddressMode(localSamplerInfo.AddressW), + .MaxAnisotropy = localSamplerInfo.MaxAnisotropy, + .ComparisonFunc = ConvertToDirectX12CompareFunction(localSamplerInfo.CompareFunction), + .BorderColor = { borderColor.Red, borderColor.Green, borderColor.Blue, borderColor.Alpha }, + .MinLOD = localSamplerInfo.MinLod, + .MaxLOD = localSamplerInfo.MaxLod == 0 ? 1000 : localSamplerInfo.MaxLod + }; + + graphicsDeviceData->Device->CreateSampler(&samplerDesc, descriptorHandle); + + auto index = ConvertDirectX12DescriptorHandleToIndex(descriptorHeap, descriptorHandle); + + if ((index % 1024) == 0) + { + SystemCommitMemory(DirectX12MemoryArena, &directX12SamplerInfos[index], 1024 * sizeof(ElemGraphicsSamplerInfo)); + } + + directX12SamplerInfos[index] = localSamplerInfo; + return index; +} + +ElemGraphicsSamplerInfo DirectX12GetGraphicsSamplerInfo(ElemGraphicsSampler sampler) +{ + InitDirectX12ResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return {}; + } + + return directX12SamplerInfos[sampler]; +} + +void DirectX12FreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) +{ + InitDirectX12ResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return; + } + + if (options && options->FencesToWait.Length > 0) + { + EnqueueResourceDeleteEntry(DirectX12MemoryArena, sampler, ResourceDeleteType_Sampler, options->FencesToWait); + return; + } + + directX12SamplerInfos[sampler] = {}; +} diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h index 99a3698c..f63d55bb 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.h @@ -79,3 +79,7 @@ void DirectX12FreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor desc void DirectX12ProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice); void DirectX12GraphicsResourceBarrier(ElemCommandList commandList, ElemGraphicsResourceDescriptor descriptor, const ElemGraphicsResourceBarrierOptions* options); + +ElemGraphicsSampler DirectX12CreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo); +ElemGraphicsSamplerInfo DirectX12GetGraphicsSamplerInfo(ElemGraphicsSampler sampler); +void DirectX12FreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options); diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp index 46301231..35b7b466 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Shader.cpp @@ -180,36 +180,6 @@ D3D12_BLEND ConvertToDirectX12Blend(ElemGraphicsBlendFactor blendFactor) } } -D3D12_COMPARISON_FUNC ConvertToDirectX12CompareFunction(ElemGraphicsCompareFunction compareFunction) -{ - switch (compareFunction) - { - case ElemGraphicsCompareFunction_Never: - return D3D12_COMPARISON_FUNC_NEVER; - - case ElemGraphicsCompareFunction_Less: - return D3D12_COMPARISON_FUNC_LESS; - - case ElemGraphicsCompareFunction_Equal: - return D3D12_COMPARISON_FUNC_EQUAL; - - case ElemGraphicsCompareFunction_LessEqual: - return D3D12_COMPARISON_FUNC_LESS_EQUAL; - - case ElemGraphicsCompareFunction_Greater: - return D3D12_COMPARISON_FUNC_GREATER; - - case ElemGraphicsCompareFunction_NotEqual: - return D3D12_COMPARISON_FUNC_NOT_EQUAL; - - case ElemGraphicsCompareFunction_GreaterEqual: - return D3D12_COMPARISON_FUNC_GREATER_EQUAL; - - case ElemGraphicsCompareFunction_Always: - return D3D12_COMPARISON_FUNC_ALWAYS; - } -} - ElemShaderLibrary DirectX12CreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) { InitDirectX12ShaderMemory(); diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 1a4503c8..a2533710 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -264,6 +264,7 @@ typedef enum typedef struct { ElemSceneCoordinateSystem CoordinateSystem; + bool FlipVerticalTextureCoordinates; float Scaling; ElemToolsVector3 Rotation; ElemToolsVector3 Translation; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index a4a4fc61..b6a080d2 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -67,7 +67,7 @@ unsigned long FastObjFileSize(void* file, void* userData) } // TODO: Remove that function and include it in construct vertex buffer -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) +void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, bool flipVerticalTextureCoordinates, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) { // TODO: Check what to include auto currentVertexBufferPointer = *vertexBufferPointer; @@ -109,6 +109,11 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc { ((float*)currentVertexBufferPointer)[j] = objMesh->texcoords[2 * objVertex.t + j]; } + + if (!flipVerticalTextureCoordinates) + { + ((float*)currentVertexBufferPointer)[1] = 1 - ((float*)currentVertexBufferPointer)[1]; + } } // TODO: Force the texture coordinates for now @@ -116,7 +121,7 @@ void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSc *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) +ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, bool flipVerticalTextureCoordinates, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) { // TODO: Config of the vertex components to load auto positionSize = sizeof(float) * 3; @@ -132,7 +137,7 @@ ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, ElemSceneCoor for (uint32_t i = meshPrimitiveInfo->IndexOffset; i < meshPrimitiveInfo->IndexOffset + meshPrimitiveInfo->IndexCount; i++) { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); + ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, flipVerticalTextureCoordinates, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); } return @@ -248,6 +253,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E if (faceMaterial != currentMaterial) { currentMaterial = faceMaterial; + printf("Material: %d\n", currentMaterial); auto meshPrimitiveIndexOffset = indexOffset + meshPrimitiveInfo->IndexCount; @@ -255,6 +261,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E *meshPrimitiveInfo = { .IndexOffset = meshPrimitiveIndexOffset, + .MaterialId = (int32_t)currentMaterial, .BoundingBox = { .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, @@ -279,7 +286,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E auto meshPrimitive = &meshPrimitives[j]; auto meshPrimitiveInfo = &meshPrimitiveInfos[j]; - meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); + meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, options->FlipVerticalTextureCoordinates, objFileData, meshPrimitiveInfo); meshPrimitive->IndexBuffer = ConstructObjIndexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); meshPrimitive->BoundingBox = meshPrimitiveInfo->BoundingBox; meshPrimitive->MaterialId = meshPrimitiveInfo->MaterialId; diff --git a/tests/GraphicsTests/ResourceTests.cpp b/tests/GraphicsTests/ResourceTests.cpp index 291f6328..da05671b 100644 --- a/tests/GraphicsTests/ResourceTests.cpp +++ b/tests/GraphicsTests/ResourceTests.cpp @@ -832,3 +832,139 @@ UTEST(Resource, GetGraphicsResourceDescriptorInfo_WithInvalidDescriptor) ASSERT_LOG_MESSAGE("Resource Descriptor is invalid."); ASSERT_EQ_MSG(descriptorInfo.Resource, 0u, "Resource should be equals to 0."); } + +// TODO: Add validation tests +// TODO: Validation MaxAnisotropy 16 + +UTEST(Resource, CreateGraphicsSampler_WithDefaultValues) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + ElemGraphicsSamplerInfo samplerInfo = {}; + + // Act + auto sampler = ElemCreateGraphicsSampler(graphicsDevice, &samplerInfo); + + // Assert + auto resultSamplerInfo = ElemGetGraphicsSamplerInfo(sampler); + + ElemFreeGraphicsSampler(sampler, nullptr); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(resultSamplerInfo.MinFilter, ElemGraphicsSamplerFilter_Nearest, "MinFilter should be equals to Nearest."); + ASSERT_EQ_MSG(resultSamplerInfo.MagFilter, ElemGraphicsSamplerFilter_Nearest, "MagFilter should be equals to Nearest."); + ASSERT_EQ_MSG(resultSamplerInfo.MipFilter, ElemGraphicsSamplerFilter_Nearest, "MipFilter should be equals to Nearest."); + ASSERT_EQ_MSG(resultSamplerInfo.AddressU, ElemGraphicsSamplerAddressMode_Repeat, "AddressU should be equals to Repeat."); + ASSERT_EQ_MSG(resultSamplerInfo.AddressV, ElemGraphicsSamplerAddressMode_Repeat, "AddressV should be equals to Repeat."); + ASSERT_EQ_MSG(resultSamplerInfo.AddressW, ElemGraphicsSamplerAddressMode_Repeat, "AddressW should be equals to Repeat."); + ASSERT_EQ_MSG(resultSamplerInfo.MaxAnisotropy, 1u, "Max Anisotropy should equals to 1."); + ASSERT_EQ_MSG(resultSamplerInfo.CompareFunction, ElemGraphicsCompareFunction_Never, "CompareFunction should equals to Never."); + ASSERT_EQ_MSG(resultSamplerInfo.BorderColor.Red, 0.0f, "Border Red Color should be 0."); + ASSERT_EQ_MSG(resultSamplerInfo.BorderColor.Green, 0.0f, "Border Green Color should be 0."); + ASSERT_EQ_MSG(resultSamplerInfo.BorderColor.Blue, 0.0f, "Border Blue Color should be 0."); + ASSERT_EQ_MSG(resultSamplerInfo.BorderColor.Alpha, 0.0f, "Border Alpha Color should be 0."); + ASSERT_EQ_MSG(resultSamplerInfo.MinLod, 0.0f, "Min Lod should be 0."); + ASSERT_EQ_MSG(resultSamplerInfo.MaxLod, 0.0f, "Max Lod should be 0."); +} + +UTEST(Resource, FreeGraphicsSampler) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + ElemGraphicsSamplerInfo samplerInfo = { .MipFilter = ElemGraphicsSamplerFilter_Linear }; + auto sampler = ElemCreateGraphicsSampler(graphicsDevice, &samplerInfo); + + // Act + ElemFreeGraphicsSampler(sampler, nullptr); + + // Assert + auto resultSamplerInfo = ElemGetGraphicsSamplerInfo(sampler); + + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(resultSamplerInfo.MaxAnisotropy, 0u, "MaxAnisotropy should be equals to 0."); +} + +UTEST(Resource, FreeGraphicsSampler_WithFenceNotExecuted) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + + ElemGraphicsSamplerInfo samplerInfo = { .MipFilter = ElemGraphicsSamplerFilter_Linear }; + auto sampler = ElemCreateGraphicsSampler(graphicsDevice, &samplerInfo); + + ElemFence fence = { .CommandQueue = commandQueue, .FenceValue = UINT64_MAX }; + ElemFreeGraphicsSamplerOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; + + // Act + ElemFreeGraphicsSampler(sampler, &options); + + // Assert + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); + auto resultSamplerInfo = ElemGetGraphicsSamplerInfo(sampler); + + ElemFreeGraphicsSampler(sampler, nullptr); + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(resultSamplerInfo.MaxAnisotropy, 1u, "MaxAnisotropy should be equals to 1."); +} + +UTEST(Resource, FreeGraphicsSampler_WithFenceExecuted) +{ + // Arrange + auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); + auto commandQueue = ElemCreateCommandQueue(graphicsDevice, ElemCommandQueueType_Graphics, nullptr); + + ElemGraphicsSamplerInfo samplerInfo = { .MipFilter = ElemGraphicsSamplerFilter_Linear }; + auto sampler = ElemCreateGraphicsSampler(graphicsDevice, &samplerInfo); + + auto commandList = ElemGetCommandList(commandQueue, nullptr); + ElemCommitCommandList(commandList); + auto fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + + ElemFreeGraphicsSamplerOptions options = { .FencesToWait = { .Items = &fence, .Length = 1 } }; + + // Act + ElemFreeGraphicsSampler(sampler, &options); + + // Assert + ElemWaitForFenceOnCpu(fence); + ElemProcessGraphicsResourceDeleteQueue(graphicsDevice); + auto resultSamplerInfo = ElemGetGraphicsSamplerInfo(sampler); + + ElemFreeCommandQueue(commandQueue); + ElemFreeGraphicsDevice(graphicsDevice); + + ASSERT_LOG_NOERROR(); + ASSERT_EQ_MSG(resultSamplerInfo.MaxAnisotropy, 0u, "MaxAnisotropy should be equals to 0."); +} + +UTEST(Resource, FreeGraphicsSampler_WithInvalidDescriptor) +{ + // Arrange + ElemGraphicsSampler sampler = -1; + + // Act + ElemFreeGraphicsSampler(sampler, nullptr); + + // Assert + ASSERT_LOG_MESSAGE("Sampler is invalid."); +} + +UTEST(Resource, GetGraphicsSamplerInfo_WithInvalidDescriptor) +{ + // Arrange + ElemGraphicsSampler sampler = -1; + + // Act + auto samplerInfo = ElemGetGraphicsSamplerInfo(sampler); + + // Assert + ASSERT_LOG_MESSAGE("Sampler is invalid."); + ASSERT_EQ_MSG(samplerInfo.MaxAnisotropy, 0u, "MaxAnisotropy should be equals to 0."); +} From ca1d8aa3a5f6ac8ba991aebd18f0bb101364a5c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sun, 12 Jan 2025 10:14:14 +0100 Subject: [PATCH 84/93] WIP Tangent space of OBJ --- samples/Common/SampleInputsCamera.h | 1 + samples/Common/SampleSceneLoader.h | 19 +- .../99-Renderer/Data/RenderMesh.hlsl | 28 +- samples/Elemental/99-Renderer/main.c | 7 +- src/ElementalTools/ElementalTools.h | 5 + .../SceneLoading/SceneLoaderObj.cpp | 246 ++++++++++++------ src/ElementalTools/ToolsUtils.cpp | 113 ++++++++ src/ElementalTools/ToolsUtils.h | 15 ++ 8 files changed, 352 insertions(+), 82 deletions(-) diff --git a/samples/Common/SampleInputsCamera.h b/samples/Common/SampleInputsCamera.h index 295b4fc9..7e33b8d7 100644 --- a/samples/Common/SampleInputsCamera.h +++ b/samples/Common/SampleInputsCamera.h @@ -38,6 +38,7 @@ typedef struct typedef struct { SampleCamera Camera; + // TODO: Instead of having a debug camera, we could "freeze" the camera like in AW2 SampleCamera DebugCamera; SampleMatrix4x4 ProjectionMatrix; SampleMatrix4x4 ViewProjMatrix; diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 685f7aa2..a0d3ff6b 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -194,6 +194,7 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM // TODO: Some materials can use the same texture so we need to load it only once if (strlen(materialHeader->AlbedoTexturePath) > 0) { + // TODO: Refactor that char fullTexturePath[MAX_PATH]; memset(fullTexturePath, 0, MAX_PATH); strcpy(fullTexturePath, directoryPath); @@ -203,15 +204,29 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM shaderMaterial->AlbedoTextureId = materialData->AlbedoTexture.GpuTexture.ReadDescriptor; } - // TODO: NormalMap + if (strlen(materialHeader->NormalTexturePath) > 0) + { + char fullTexturePath[MAX_PATH]; + memset(fullTexturePath, 0, MAX_PATH); + strcpy(fullTexturePath, directoryPath); + strcat(fullTexturePath, materialHeader->NormalTexturePath); + + SampleLoadTexture(fullTexturePath, &materialData->NormalTexture, gpuMemory); + shaderMaterial->NormalTextureId = materialData->NormalTexture.GpuTexture.ReadDescriptor; + } } void SampleFreeMaterial(SampleMaterialData* materialData) { - if (materialData->AlbedoTexture.IsLoaded) + if (materialData->AlbedoTexture.GpuTexture.Texture) { SampleFreeTexture(&materialData->AlbedoTexture); } + + if (materialData->NormalTexture.GpuTexture.Texture) + { + SampleFreeTexture(&materialData->NormalTexture); + } } void SampleLoadScene(const char* path, SampleSceneData* sceneData, SampleGpuMemory* gpuMemory) diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index ca4a4782..fa85ab26 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -41,10 +41,12 @@ struct ElemMeshlet uint32_t TriangleCount; }; +// Compress Data struct Vertex { float3 Position; float3 Normal; + float4 Tangent; float2 TextureCoordinates; }; @@ -52,6 +54,7 @@ struct VertexOutput { float4 Position: SV_Position; float3 WorldNormal: Attribute0; + float4 Tangent: Attribute5; float2 TextureCoordinates: Attribute1; nointerpolation uint MeshletIndex: Attribute2; nointerpolation uint MaterialId: Attribute3; @@ -95,6 +98,7 @@ void MeshMain(in uint groupId: SV_GroupID, // NOTE: This calculation is faster because v * M is faster than M * M vertices[groupThreadId].Position = mul(float4(worldPosition, 1.0), frameData.ViewProjMatrix); vertices[groupThreadId].WorldNormal = worldNormal; + vertices[groupThreadId].Tangent = vertex.Tangent; vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; vertices[groupThreadId].MeshletIndex = groupId; vertices[groupThreadId].MaterialId = parameters.MaterialId; @@ -134,9 +138,13 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 if (frameData.ShowMeshlets == 0) { + float3 worldNormal = normalize(input.WorldNormal); + if (material.AlbedoTextureId >= 0) { - Texture2D albedoTexture = ResourceDescriptorHeap[material.AlbedoTextureId]; + // TODO: Should we use non uniform index here? We know we have the same material for each meshlet. + // But we sometimes may group some meshlets together + Texture2D albedoTexture = ResourceDescriptorHeap[NonUniformResourceIndex(material.AlbedoTextureId)]; float4 albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates); // TODO: Doing discard on the main pass is really bad for performance. @@ -148,11 +156,22 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 discard; } - return albedo; + //return albedo; + } + + if (material.NormalTextureId >= 0) + { + Texture2D normalTexture = ResourceDescriptorHeap[NonUniformResourceIndex(material.NormalTextureId)]; + float3 normalMap = normalTexture.Sample(textureSampler, input.TextureCoordinates).rgb * 2.0 - 1.0; + + float3 bitangent = cross(worldNormal, input.Tangent.xyz) * input.Tangent.w; + worldNormal = normalize(normalMap.r * input.Tangent.xyz + normalMap.g * bitangent + normalMap.b * worldNormal); } - return float4(0, 0, 1, 1);; - return float4(normalize(input.WorldNormal) * 0.5 + 0.5, 1.0); + + //return float4(0, 0, 1, 1);; + //return float4(input.Tangent * 0.5 + 0.5, 1.0); + return float4(worldNormal * 0.5 + 0.5, 1.0); } else { @@ -160,6 +179,7 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 uint hashResult = hash(input.MaterialId); float3 meshletColor = float3(float(hashResult & 255), float((hashResult >> 8) & 255), float((hashResult >> 16) & 255)) / 255.0; + return float4(input.WorldNormal * 0.5 + 0.5, 1.0); return float4(meshletColor, 1.0); } } diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index a1a87b95..af79c925 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -134,7 +134,7 @@ void InitSample(void* payload) .ShaderLibrary = shaderLibrary, .MeshShaderFunction = "MeshMain", .PixelShaderFunction = "PixelMain", - .CullMode = ElemGraphicsCullMode_None, // TODO: We need to deactivate cull only for transparent objects! + //.CullMode = ElemGraphicsCullMode_None, // TODO: We need to deactivate cull only for transparent objects! .RenderTargets = { .Items = (ElemGraphicsPipelineStateRenderTarget[]) {{ .Format = swapChainInfo.Format }}, .Length = 1 }, .DepthStencil = { @@ -241,6 +241,11 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void { SampleLoadTextureData(loadDataCommandList, &material->AlbedoTexture, &applicationPayload->GpuMemory); } + + if (material->NormalTexture.Path && !material->NormalTexture.IsLoaded) + { + SampleLoadTextureData(loadDataCommandList, &material->NormalTexture, &applicationPayload->GpuMemory); + } } for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index a2533710..5257b31e 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -120,6 +120,11 @@ typedef struct uint32_t Length; } ElemToolsMessageSpan; +typedef struct +{ + float X, Y; +} ElemToolsVector2; + typedef struct { float X, Y, Z; diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index b6a080d2..5989d8bd 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -15,7 +15,14 @@ struct ObjMeshPrimitiveInfo uint32_t IndexOffset; uint32_t IndexCount; int32_t MaterialId; - ElemToolsBoundingBox BoundingBox; +}; + +struct ObjVertex +{ + ElemToolsVector3 Position; + ElemToolsVector3 Normal; + ElemToolsVector4 Tangent; + ElemToolsVector2 TextureCoordinates; }; void* FastObjFileOpen(const char* path, void* userData) @@ -66,109 +73,215 @@ unsigned long FastObjFileSize(void* file, void* userData) return objLoaderFileData->FileData.Length; } -// TODO: Remove that function and include it in construct vertex buffer -void ProcessObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, bool flipVerticalTextureCoordinates, uint8_t** vertexBufferPointer, ElemToolsBoundingBox* boundingBox) + +ObjVertex ReadObjVertex(fastObjIndex objVertex, const fastObjMesh* objMesh, ElemSceneCoordinateSystem coordinateSystem, bool flipVerticalTextureCoordinates) { - // TODO: Check what to include - auto currentVertexBufferPointer = *vertexBufferPointer; + ObjVertex result = {}; if (objVertex.p) { - for (uint32_t j = 0; j < 3; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->positions[3 * objVertex.p + j]; - } + result.Position.X = objMesh->positions[3 * objVertex.p]; + result.Position.Y = objMesh->positions[3 * objVertex.p + 1]; + result.Position.Z = objMesh->positions[3 * objVertex.p + 2]; if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { - ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + result.Position.Z = -result.Position.Z; } - - AddPointToBoundingBox({ ((float*)currentVertexBufferPointer)[0], ((float*)currentVertexBufferPointer)[1], ((float*)currentVertexBufferPointer)[2]}, boundingBox); - currentVertexBufferPointer += 3 * sizeof(float); } if (objVertex.n) { - for (uint32_t j = 0; j < 3; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->normals[3 * objVertex.n + j]; - } + result.Normal.X = objMesh->normals[3 * objVertex.n]; + result.Normal.Y = objMesh->normals[3 * objVertex.n + 1]; + result.Normal.Z = objMesh->normals[3 * objVertex.n + 2]; if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { - ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + result.Normal.Z = -result.Normal.Z; } - - currentVertexBufferPointer += 3 * sizeof(float); } if (objVertex.t) { - for (uint32_t j = 0; j < 2; j++) - { - ((float*)currentVertexBufferPointer)[j] = objMesh->texcoords[2 * objVertex.t + j]; - } + result.TextureCoordinates.X = objMesh->texcoords[2 * objVertex.t]; + result.TextureCoordinates.Y = objMesh->texcoords[2 * objVertex.t + 1]; if (!flipVerticalTextureCoordinates) { - ((float*)currentVertexBufferPointer)[1] = 1 - ((float*)currentVertexBufferPointer)[1]; + result.TextureCoordinates.Y = 1 - result.TextureCoordinates.Y; } } - // TODO: Force the texture coordinates for now - currentVertexBufferPointer += 2 * sizeof(float); + return result; +} + +// TODO: Pass vertex format configured by the lib +void WriteObjVertex(const ObjVertex* vertex, uint8_t** vertexBufferPointer) +{ + // TODO: Check what to include + auto currentVertexBufferPointer = *vertexBufferPointer; + + // Positions + ((float*)currentVertexBufferPointer)[0] = vertex->Position.X; + ((float*)currentVertexBufferPointer)[1] = vertex->Position.Y; + ((float*)currentVertexBufferPointer)[2] = vertex->Position.Z; + + currentVertexBufferPointer += sizeof(ElemToolsVector3); + + // Normals + ((float*)currentVertexBufferPointer)[0] = vertex->Normal.X; + ((float*)currentVertexBufferPointer)[1] = vertex->Normal.Y; + ((float*)currentVertexBufferPointer)[2] = vertex->Normal.Z; + + currentVertexBufferPointer += sizeof(ElemToolsVector3); + + // Tangents + ((float*)currentVertexBufferPointer)[0] = vertex->Tangent.X; + ((float*)currentVertexBufferPointer)[1] = vertex->Tangent.Y; + ((float*)currentVertexBufferPointer)[2] = vertex->Tangent.Z; + ((float*)currentVertexBufferPointer)[3] = vertex->Tangent.W; + + currentVertexBufferPointer += sizeof(ElemToolsVector4); + + // Texture Coordinates + ((float*)currentVertexBufferPointer)[0] = vertex->TextureCoordinates.X; + ((float*)currentVertexBufferPointer)[1] = vertex->TextureCoordinates.Y; + + currentVertexBufferPointer += sizeof(ElemToolsVector2); + *vertexBufferPointer = currentVertexBufferPointer; } -ElemVertexBuffer ConstructObjVertexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, bool flipVerticalTextureCoordinates, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) +struct ObjTriangle +{ + uint32_t Vertices[3]; + // TODO: More data +}; + +// TODO: Generalize this function so that it can be used by other loaders +ElemToolsVector3 ComputeTriangleTangentVector(const ObjTriangle* triangle, ReadOnlySpan vertexList) +{ + // TODO: Refactor that + ObjVertex triangleVertices[3]; + + for (uint32_t j = 0; j < 3; j++) + { + triangleVertices[j] = vertexList[triangle->Vertices[j]]; + } + + // TODO: Put that in a function because we may use it of GLTF too if the tangents are missing + // Compute tangents + auto edge1 = triangleVertices[1].Position - triangleVertices[0].Position; + auto edge2 = triangleVertices[2].Position - triangleVertices[0].Position; + + auto deltaUV1 = triangleVertices[1].TextureCoordinates - triangleVertices[0].TextureCoordinates; + auto deltaUV2 = triangleVertices[2].TextureCoordinates - triangleVertices[0].TextureCoordinates; + + float f = 1.0f / (deltaUV1.X * deltaUV2.Y - deltaUV2.X * deltaUV1.Y); + + ElemToolsVector3 tangent = {}; + + tangent.X = f * (deltaUV2.Y * edge1.X - deltaUV1.Y * edge2.X); + tangent.Y = f * (deltaUV2.Y * edge1.Y - deltaUV1.Y * edge2.Y); + tangent.Z = f * (deltaUV2.Y * edge1.Z - deltaUV1.Y * edge2.Z); + + return tangent; +} + +ElemSceneMeshPrimitive ConstructObjMeshPrimitive(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPrimitiveInfo* meshPrimitiveInfo, const ElemLoadSceneOptions* options) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + + ElemSceneMeshPrimitive result = + { + .BoundingBox = + { + .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, + .MaxPoint = { FLT_MIN, FLT_MIN, FLT_MIN } + } + }; + // TODO: Config of the vertex components to load auto positionSize = sizeof(float) * 3; auto normalSize = sizeof(float) * 3; + auto tangentSize = sizeof(float) * 4; auto textureCoordinatesSize = sizeof(float) * 2; - auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; + auto maxVertexSize = positionSize + normalSize + tangentSize + textureCoordinatesSize; // TODO: Temporary auto realVertexSize = maxVertexSize; - auto vertexBuffer = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount * maxVertexSize); - auto currentVertexBufferPointer = vertexBuffer.Pointer; + auto indexBufferData = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount); - for (uint32_t i = meshPrimitiveInfo->IndexOffset; i < meshPrimitiveInfo->IndexOffset + meshPrimitiveInfo->IndexCount; i++) - { - ProcessObjVertex(objMesh->indices[i], objMesh, coordinateSystem, flipVerticalTextureCoordinates, ¤tVertexBufferPointer, &meshPrimitiveInfo->BoundingBox); - } + auto triangleCount = meshPrimitiveInfo->IndexCount / 3; + auto triangleList = SystemPushArray(stackMemoryArena, triangleCount); + auto vertexList = SystemPushArray(stackMemoryArena, 3 * triangleCount); + auto vertexCount = 0u; - return + for (uint32_t i = 0; i < triangleCount; i++) { - .Data = { .Items = vertexBuffer.Pointer, .Length = (uint32_t)vertexBuffer.Length }, - .VertexSize = (uint32_t)realVertexSize, - .VertexCount = meshPrimitiveInfo->IndexCount - }; -} + auto triangle = &triangleList[i]; -ElemUInt32Span ConstructObjIndexBuffer(MemoryArena memoryArena, ElemSceneCoordinateSystem coordinateSystem, const fastObjMesh* objMesh, ObjMeshPrimitiveInfo* meshPrimitiveInfo) -{ - auto indexBuffer = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount); + for (uint32_t j = 0; j < 3; j++) + { + auto vertex = ReadObjVertex(objMesh->indices[meshPrimitiveInfo->IndexOffset + i * 3 + j], objMesh, options->CoordinateSystem, options->FlipVerticalTextureCoordinates); - for (uint32_t i = 0; i < meshPrimitiveInfo->IndexCount; i += 3) - { - indexBuffer[i] = i; + // TODO: Check for duplicates + auto vertexIndex = vertexCount++; + vertexList[vertexIndex] = vertex; + AddPointToBoundingBox(vertex.Position, &result.BoundingBox); - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + triangle->Vertices[j] = vertexIndex; + indexBufferData[i * 3 + j] = vertexIndex; + } + + if (options->CoordinateSystem == ElemSceneCoordinateSystem_LeftHanded) { - indexBuffer[i + 1] = i + 2; - indexBuffer[i + 2] = i + 1; + auto tmp = indexBufferData[i * 3 + 1]; + indexBufferData[i * 3 + 1] = indexBufferData[i * 3 + 2]; + indexBufferData[i * 3 + 2] = tmp; } - else + + auto triangleTangent = ComputeTriangleTangentVector(triangle, vertexList); + + for (uint32_t j = 0; j < 3; j++) { - indexBuffer[i + 1] = i + 1; - indexBuffer[i + 2] = i + 2; + auto vertexIndex = triangle->Vertices[j]; + vertexList[vertexIndex].Tangent.XYZ = vertexList[vertexIndex].Tangent.XYZ + triangleTangent; } } + + auto vertexBufferData = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount * maxVertexSize); + auto currentVertexBufferPointer = vertexBufferData.Pointer; + + for (uint32_t i = 0; i < vertexCount; i++) + { + auto normal = vertexList[i].Normal; + auto tangent = ElemToolsNormalizeV3(vertexList[i].Tangent.XYZ); + + // TODO: Active that when we have unique vertices + //tangent = ElemToolsNormalizeV3(tangent - normal * ElemToolsDotProductV3(normal, tangent)); + + // TODO: Process vertex data + vertexList[i].Tangent.XYZ = tangent; + vertexList[i].Tangent.W = 1.0f; + //float handedness = (glm::dot(glm::cross(normal, tangent), B) < 0.0f) ? -1.0f : 1.0f; + + WriteObjVertex(&vertexList[i], ¤tVertexBufferPointer); + } - return { .Items = indexBuffer.Pointer, .Length = (uint32_t)indexBuffer.Length }; + result.VertexBuffer = + { + .Data = { .Items = vertexBufferData.Pointer, .Length = (uint32_t)vertexBufferData.Length }, + .VertexSize = (uint32_t)realVertexSize, + .VertexCount = meshPrimitiveInfo->IndexCount + }; + + result.IndexBuffer = { .Items = indexBufferData.Pointer, .Length = (uint32_t)indexBufferData.Length }; + result.MaterialId = meshPrimitiveInfo->MaterialId; + + return result; } void ApplyObjMeshPrimitiveInverseTranslation(ElemToolsVector3 translation, ElemVertexBuffer* vertexBuffer) @@ -228,12 +341,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E *meshPrimitiveInfo = { .IndexOffset = indexOffset, - .MaterialId = (int32_t)currentMaterial, - .BoundingBox = - { - .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, - .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } - } + .MaterialId = (int32_t)currentMaterial }; for (uint32_t j = currentFaceStart; j < currentFaceEnd; j++) @@ -261,12 +369,7 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E *meshPrimitiveInfo = { .IndexOffset = meshPrimitiveIndexOffset, - .MaterialId = (int32_t)currentMaterial, - .BoundingBox = - { - .MinPoint = { FLT_MAX, FLT_MAX, FLT_MAX }, - .MaxPoint = { FLT_MIN, FLT_MIN, FLT_MIN } - } + .MaterialId = (int32_t)currentMaterial }; } @@ -283,15 +386,8 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E for (uint32_t j = 0; j < meshPrimitiveCount; j++) { - auto meshPrimitive = &meshPrimitives[j]; - auto meshPrimitiveInfo = &meshPrimitiveInfos[j]; - - meshPrimitive->VertexBuffer = ConstructObjVertexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, options->FlipVerticalTextureCoordinates, objFileData, meshPrimitiveInfo); - meshPrimitive->IndexBuffer = ConstructObjIndexBuffer(sceneLoaderMemoryArena, options->CoordinateSystem, objFileData, meshPrimitiveInfo); - meshPrimitive->BoundingBox = meshPrimitiveInfo->BoundingBox; - meshPrimitive->MaterialId = meshPrimitiveInfo->MaterialId; - - AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); + meshPrimitives[j] = ConstructObjMeshPrimitive(sceneLoaderMemoryArena, objFileData, &meshPrimitiveInfos[j], options); + AddBoundingBoxToBoundingBox(&meshPrimitives[j].BoundingBox, &mesh->BoundingBox); } auto boundingBoxCenter = GetBoundingBoxCenter(&mesh->BoundingBox); diff --git a/src/ElementalTools/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp index e47f39d3..fb6d2ee5 100644 --- a/src/ElementalTools/ToolsUtils.cpp +++ b/src/ElementalTools/ToolsUtils.cpp @@ -97,6 +97,119 @@ ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox) }; } +ElemToolsVector2 operator +(const ElemToolsVector2& v1, const ElemToolsVector2& v2) +{ + ElemToolsVector2 result; + + result.X = v1.X + v2.X; + result.Y = v1.Y + v2.Y; + + return result; +} + +ElemToolsVector2 operator -(const ElemToolsVector2& v1, const ElemToolsVector2& v2) +{ + ElemToolsVector2 result; + + result.X = v1.X - v2.X; + result.Y = v1.Y - v2.Y; + + return result; +} + +ElemToolsVector3 operator +(const ElemToolsVector3& v1, const ElemToolsVector3& v2) +{ + ElemToolsVector3 result; + + result.X = v1.X + v2.X; + result.Y = v1.Y + v2.Y; + result.Z = v1.Z + v2.Z; + + return result; +} + +ElemToolsVector3 operator -(const ElemToolsVector3& v1, const ElemToolsVector3& v2) +{ + ElemToolsVector3 result; + + result.X = v1.X - v2.X; + result.Y = v1.Y - v2.Y; + result.Z = v1.Z - v2.Z; + + return result; +} + +ElemToolsVector3 operator *(const ElemToolsVector3& v1, const ElemToolsVector3& v2) +{ + ElemToolsVector3 result; + + result.X = v1.X * v2.X; + result.Y = v1.Y * v2.Y; + result.Z = v1.Z * v2.Z; + + return result; +} + +ElemToolsVector3 operator *(const ElemToolsVector3& v, float scalar) +{ + ElemToolsVector3 result; + + result.X = v.X * scalar; + result.Y = v.Y * scalar; + result.Z = v.Z * scalar; + + return result; +} + +ElemToolsVector3 ElemToolsMulScalarV3(ElemToolsVector3 v, float scalar) +{ + ElemToolsVector3 result; + + result.X = v.X * scalar; + result.Y = v.Y * scalar; + result.Z = v.Z * scalar; + + return result; +} + +ElemToolsVector3 ElemToolsMulV3(ElemToolsVector3 v1, ElemToolsVector3 v2) +{ + ElemToolsVector3 result; + + result.X = v1.X * v2.X; + result.Y = v1.Y * v2.Y; + result.Z = v1.Z * v2.Z; + + return result; +} + +float ElemToolsDotProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2) +{ + return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z; +} + +float ElemToolsMagnitudeSquaredV3(ElemToolsVector3 v) +{ + return ElemToolsDotProductV3(v, v); +} + +float ElemToolsMagnitudeV3(ElemToolsVector3 v) +{ + return sqrtf(ElemToolsMagnitudeSquaredV3(v)); +} + +ElemToolsVector3 ElemToolsNormalizeV3(ElemToolsVector3 v) +{ + float magnitude = ElemToolsMagnitudeV3(v); + + if (magnitude > 0.0f) + { + ElemToolsVector3 result = ElemToolsMulScalarV3(v, (1.0f / ElemToolsMagnitudeV3(v))); + return result; + } + + return v; +} // TODO: CLEANUP ElemToolsVector3 ElemToolsCrossProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2) { diff --git a/src/ElementalTools/ToolsUtils.h b/src/ElementalTools/ToolsUtils.h index 6ce9874e..b644d73f 100644 --- a/src/ElementalTools/ToolsUtils.h +++ b/src/ElementalTools/ToolsUtils.h @@ -13,6 +13,21 @@ void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundin void AddBoundingBoxToBoundingBox(const ElemToolsBoundingBox* additional, ElemToolsBoundingBox* boundingBox); ElemToolsVector3 GetBoundingBoxCenter(const ElemToolsBoundingBox* boundingBox); +ElemToolsVector3 ElemToolsMulScalarV3(ElemToolsVector3 v, float scalar); +ElemToolsVector3 ElemToolsMulV3(ElemToolsVector3 v1, ElemToolsVector3 v2); +float ElemToolsDotProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2); +float ElemToolsMagnitudeSquaredV3(ElemToolsVector3 v); +float ElemToolsMagnitudeV3(ElemToolsVector3 v); +ElemToolsVector3 ElemToolsNormalizeV3(ElemToolsVector3 v); + +ElemToolsVector2 operator +(const ElemToolsVector2& v1, const ElemToolsVector2& v2); +ElemToolsVector2 operator -(const ElemToolsVector2& v1, const ElemToolsVector2& v2); + +ElemToolsVector3 operator +(const ElemToolsVector3& v1, const ElemToolsVector3& v2); +ElemToolsVector3 operator -(const ElemToolsVector3& v1, const ElemToolsVector3& v2); +ElemToolsVector3 operator *(const ElemToolsVector3& v1, const ElemToolsVector3& v2); +ElemToolsVector3 operator *(const ElemToolsVector3& v1, float scalar); + ElemToolsVector4 ElemToolsCreateQuaternion(ElemToolsVector3 v, float w); ElemToolsVector4 ElemToolsMulQuat(ElemToolsVector4 q1, ElemToolsVector4 q2); ElemToolsMatrix4x4 ElemToolsCreateRotationMatrix(ElemToolsVector4 quaternion); From ddd3e8431b5559acb45bab0d6e5b1e057399cce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Tue, 14 Jan 2025 14:52:26 +0100 Subject: [PATCH 85/93] Rework OBJ loading, GLTF loading, tangents and textures --- .gitignore | 3 + .gitmodules | 3 + external/CMakeLists.txt | 10 +- external/MikkTSpace | 1 + samples/Common/SampleSceneLoader.h | 61 +++- samples/Common/SampleUtils.h | 4 +- .../99-Renderer/Data/RenderMesh.hlsl | 33 ++- samples/Elemental/99-Renderer/main.c | 19 +- .../ElementalTools/02-SceneCompiler/main.c | 4 + .../ElementalTools/03-TextureCompiler/main.c | 59 ++-- .../Apple/Graphics/MetalResource.cpp | 6 + .../Common/Graphics/Vulkan/VulkanResource.cpp | 3 + src/Elemental/Elemental.h | 1 + .../Microsoft/Graphics/DirectX12Resource.cpp | 6 + src/ElementalTools/ElementalTools.h | 4 + .../Platforms/Apple/PreCompiledHeader.h | 2 + .../Platforms/Linux/PreCompiledHeader.h | 1 + .../Platforms/Microsoft/PreCompiledHeader.h | 1 + .../SceneLoading/SceneLoaderGltf.cpp | 260 +++++++++++++----- .../SceneLoading/SceneLoaderObj.cpp | 215 ++++++++------- .../SceneLoading/TangentSpaceGenerator.cpp | 81 ++++++ .../SceneLoading/TangentSpaceGenerator.h | 16 ++ src/ElementalTools/Textures/TextureLoader.cpp | 19 ++ .../Textures/TextureLoaderDds.cpp | 190 +++++++++++++ .../Textures/TextureLoaderStb.cpp | 1 - src/ElementalTools/ToolsUtils.cpp | 16 +- src/ElementalTools/ToolsUtils.h | 10 + src/ElementalTools/UnityBuild.cpp | 7 + utilities/cmake/AppPackaging.cmake | 3 + 29 files changed, 819 insertions(+), 220 deletions(-) create mode 160000 external/MikkTSpace create mode 100644 src/ElementalTools/SceneLoading/TangentSpaceGenerator.cpp create mode 100644 src/ElementalTools/SceneLoading/TangentSpaceGenerator.h create mode 100644 src/ElementalTools/Textures/TextureLoaderDds.cpp diff --git a/.gitignore b/.gitignore index ab427c02..4ff7342a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ *.gltf *.bin *.tga +*.png +*.jpg +*.dds *.mtl xcuserdata/ diff --git a/.gitmodules b/.gitmodules index 9afba37b..9ecbeb89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "external/bc7enc_rdo"] path = external/bc7enc_rdo url = https://github.com/richgel999/bc7enc_rdo.git +[submodule "external/MikkTSpace"] + path = external/MikkTSpace + url = https://github.com/mmikk/MikkTSpace.git diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index eec265ee..d684db5f 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -125,6 +125,14 @@ if(NOT BUILD_FOR_IOS) target_include_directories(stb INTERFACE ./stb/) endif() +#======================================================================= +# MikkTSpace +#======================================================================= +if(NOT BUILD_FOR_IOS) + add_library(mikktspace INTERFACE) + target_include_directories(mikktspace INTERFACE ./MikkTSpace/) +endif() + #======================================================================= # bc7encoder_rdo #======================================================================= @@ -142,7 +150,7 @@ endif() # Tools External Dependencies #======================================================================= add_library(tools_external_dependencies INTERFACE) -target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf stb bc7enc) +target_link_libraries(tools_external_dependencies INTERFACE tools_shader_compilers meshoptimizer fast_obj cgltf stb mikktspace bc7enc) #======================================================================= # xxHash diff --git a/external/MikkTSpace b/external/MikkTSpace new file mode 160000 index 00000000..3e895b49 --- /dev/null +++ b/external/MikkTSpace @@ -0,0 +1 @@ +Subproject commit 3e895b49d05ea07e4c2133156cfa94369e19e409 diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index a0d3ff6b..2f988aaa 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -27,8 +27,8 @@ typedef struct typedef struct { SampleSceneMaterialHeader MaterialHeader; - SampleTextureData AlbedoTexture; - SampleTextureData NormalTexture; + SampleTextureData* AlbedoTexture; + SampleTextureData* NormalTexture; } SampleMaterialData; // TODO: Do otherwise @@ -50,6 +50,11 @@ typedef struct SampleGpuBuffer MaterialBuffer; } SampleSceneData; +// TODO: Change that. For now we use that simple implementation +#define MAX_TEXTURE_COUNT 2048 +SampleTextureData TextureCache[MAX_TEXTURE_COUNT]; +uint32_t CurrentTextureCacheIndex = 0u; + void SampleLoadMesh(const char* path, uint32_t offset, SampleMeshData* meshData) { *meshData = (SampleMeshData){}; @@ -111,19 +116,46 @@ void SampleFreeMesh(SampleMeshData* meshData) // TODO: Free mallocs } -void SampleLoadTexture(const char* path, SampleTextureData* textureData, SampleGpuMemory* gpuMemory) +void SampleLoadTexture(const char* path, SampleTextureData** textureDataPointer, SampleGpuMemory* gpuMemory, bool isSrgb) { // TODO: Here we need to use a global list of texture that are unique based on the path // For now the same texture is loaded multiple time which is really bad + for (uint32_t i = 0; i < CurrentTextureCacheIndex; i++) + { + SampleTextureData* cacheTexture = &TextureCache[i]; + + if (strstr(cacheTexture->Path, path)) + { + printf("Found texture in cache!\n"); + *textureDataPointer = cacheTexture; + return; + } + } + + + if (CurrentTextureCacheIndex + 1 > MAX_TEXTURE_COUNT - 1) + { + printf("Texture cache at max!!!\n"); + } + + *textureDataPointer = &TextureCache[CurrentTextureCacheIndex++]; + SampleTextureData* textureData = *textureDataPointer; *textureData = (SampleTextureData){}; + printf("Loading Texture: %s\n", path); // TODO: Do better here char* tmp = malloc(strlen(path) + 1); strcpy(tmp, path); textureData->Path = tmp; FILE* file = SampleOpenFile(path, true); + + if (!file) + { + printf("NO TEXT: %s\n", path); + return; + } assert(file); fread(&textureData->TextureHeader, sizeof(SampleTextureHeader), 1, file); @@ -136,7 +168,10 @@ void SampleLoadTexture(const char* path, SampleTextureData* textureData, SampleG // TODO: For now we create the texture here but later, we should do it on the fly and update the material buffer // TODO: Get texture name without folder and extension // TODO: Get the correct format - textureData->GpuTexture = SampleCreateGpuTexture(gpuMemory, textureData->TextureHeader.Width, textureData->TextureHeader.Height, textureData->TextureHeader.MipCount, ElemGraphicsFormat_BC7_SRGB, textureData->Path); + + // TODO: We need to take into account the format of the texture + ElemGraphicsFormat format = isSrgb ? ElemGraphicsFormat_BC7_SRGB : ElemGraphicsFormat_BC7; + textureData->GpuTexture = SampleCreateGpuTexture(gpuMemory, textureData->TextureHeader.Width, textureData->TextureHeader.Height, textureData->TextureHeader.MipCount, format, textureData->Path); } // TODO: In the future we will need to load individual mips from disk @@ -190,6 +225,8 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM shaderMaterial->AlbedoFactor = materialData->MaterialHeader.AlbedoFactor; shaderMaterial->AlbedoTextureId = -1; shaderMaterial->NormalTextureId = -1; + + printf("Loading Material: %s\n", materialData->MaterialHeader.Name); // TODO: Some materials can use the same texture so we need to load it only once if (strlen(materialHeader->AlbedoTexturePath) > 0) @@ -200,8 +237,8 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM strcpy(fullTexturePath, directoryPath); strcat(fullTexturePath, materialHeader->AlbedoTexturePath); - SampleLoadTexture(fullTexturePath, &materialData->AlbedoTexture, gpuMemory); - shaderMaterial->AlbedoTextureId = materialData->AlbedoTexture.GpuTexture.ReadDescriptor; + SampleLoadTexture(fullTexturePath, &materialData->AlbedoTexture, gpuMemory, true); + shaderMaterial->AlbedoTextureId = materialData->AlbedoTexture->GpuTexture.ReadDescriptor; } if (strlen(materialHeader->NormalTexturePath) > 0) @@ -211,21 +248,21 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM strcpy(fullTexturePath, directoryPath); strcat(fullTexturePath, materialHeader->NormalTexturePath); - SampleLoadTexture(fullTexturePath, &materialData->NormalTexture, gpuMemory); - shaderMaterial->NormalTextureId = materialData->NormalTexture.GpuTexture.ReadDescriptor; + SampleLoadTexture(fullTexturePath, &materialData->NormalTexture, gpuMemory, false); + shaderMaterial->NormalTextureId = materialData->NormalTexture->GpuTexture.ReadDescriptor; } } void SampleFreeMaterial(SampleMaterialData* materialData) { - if (materialData->AlbedoTexture.GpuTexture.Texture) + if (materialData->AlbedoTexture->GpuTexture.Texture) { - SampleFreeTexture(&materialData->AlbedoTexture); + SampleFreeTexture(materialData->AlbedoTexture); } - if (materialData->NormalTexture.GpuTexture.Texture) + if (materialData->NormalTexture->GpuTexture.Texture) { - SampleFreeTexture(&materialData->NormalTexture); + SampleFreeTexture(materialData->NormalTexture); } } diff --git a/samples/Common/SampleUtils.h b/samples/Common/SampleUtils.h index cf1e64b2..2aeab7c4 100644 --- a/samples/Common/SampleUtils.h +++ b/samples/Common/SampleUtils.h @@ -190,7 +190,7 @@ void ReplaceFileExtension(const char* path, const char* extension, char* destina destination[i] = path[i] == '\\' ? '/' : path[i]; } - strncat(destination, extension, prefixLength); + strncat(destination, extension, strlen(path)); } void GetFileDirectory(const char* path, char* destination, uint32_t destinationSize) @@ -234,7 +234,7 @@ void GetRelativeResourcePath(const char* mainPath, const char* path, const char* startPath = resourcePath + mainDirectoryLength; } - strncpy(destination, startPath, startPath - resourcePath); + strncpy(destination, startPath, strlen(resourcePath) - (startPath - resourcePath)); } // ----------------------------------------------------------------------------- diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index fa85ab26..9f0c7539 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -93,12 +93,13 @@ void MeshMain(in uint groupId: SV_GroupID, float3 worldPosition = RotateQuaternion(vertex.Position, parameters.Rotation) * parameters.Scale + parameters.Translation; float3 worldNormal = RotateQuaternion(vertex.Normal, parameters.Rotation); + float3 worldTangent = RotateQuaternion(vertex.Tangent.xyz, parameters.Rotation); //vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), mul(worldMatrix, frameData.ViewProjMatrix)); // NOTE: This calculation is faster because v * M is faster than M * M vertices[groupThreadId].Position = mul(float4(worldPosition, 1.0), frameData.ViewProjMatrix); vertices[groupThreadId].WorldNormal = worldNormal; - vertices[groupThreadId].Tangent = vertex.Tangent; + vertices[groupThreadId].Tangent = float4(worldTangent, vertex.Tangent.w); vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; vertices[groupThreadId].MeshletIndex = groupId; vertices[groupThreadId].MaterialId = parameters.MaterialId; @@ -136,16 +137,17 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 SamplerState textureSampler = SamplerDescriptorHeap[parameters.TextureSampler]; - if (frameData.ShowMeshlets == 0) + if (frameData.ShowMeshlets == 0 && parameters.MaterialId >= 0) { - float3 worldNormal = normalize(input.WorldNormal); + float3 worldNormal = input.WorldNormal; + float4 albedo = float4(1, 1, 1, 1); if (material.AlbedoTextureId >= 0) { // TODO: Should we use non uniform index here? We know we have the same material for each meshlet. // But we sometimes may group some meshlets together Texture2D albedoTexture = ResourceDescriptorHeap[NonUniformResourceIndex(material.AlbedoTextureId)]; - float4 albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates); + albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates); // TODO: Doing discard on the main pass is really bad for performance. // Doing it disable the early depth test in the shader, so all pixel shader code has to run for @@ -156,22 +158,39 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 discard; } - //return albedo; + // return albedo; } if (material.NormalTextureId >= 0) { Texture2D normalTexture = ResourceDescriptorHeap[NonUniformResourceIndex(material.NormalTextureId)]; float3 normalMap = normalTexture.Sample(textureSampler, input.TextureCoordinates).rgb * 2.0 - 1.0; + //return float4(normalMap, 1); float3 bitangent = cross(worldNormal, input.Tangent.xyz) * input.Tangent.w; - worldNormal = normalize(normalMap.r * input.Tangent.xyz + normalMap.g * bitangent + normalMap.b * worldNormal); + worldNormal = normalize(normalMap.x * input.Tangent.xyz + normalMap.y * bitangent + normalMap.z * worldNormal); + + //float3 surfaceGradient = float3(TspaceNormalToDerivative(normalMap), 0); + //worldNormal = ResolveNormalFromSurfaceGradient(worldNormal, surfaceGradient); } + float nDotL = dot(worldNormal, normalize(float3(-0.5, 1, -0.5))); + float ambient = 0.6; + + return albedo * (nDotL + ambient); + return float4(worldNormal * 0.5 + 0.5, 1.0); + + if (input.Tangent.w > 0) + { + return float4(1, 0, 0, 1); + } + else + { + return float4(0, 1, 0, 1); + } //return float4(0, 0, 1, 1);; //return float4(input.Tangent * 0.5 + 0.5, 1.0); - return float4(worldNormal * 0.5 + 0.5, 1.0); } else { diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index af79c925..8d03b5fc 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -108,7 +108,7 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues // TODO: Having GPU Upload is still annoying 😞 - applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(256)); + applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(2048)); applicationPayload->FrameDataBuffer = SampleCreateGpuBufferAndUploadData(&applicationPayload->GpuMemory, &applicationPayload->FrameData, sizeof(ShaderFrameData), "FrameData"); applicationPayload->ShaderParameters.FrameDataBuffer = applicationPayload->FrameDataBuffer.ReadDescriptor; @@ -233,21 +233,30 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void // TODO: Fow now, we load all the material textures in one shot // We do it in the main loop now because later it will be a streaming system. // Doing it in the main loop force us to decouple the loading + uint32_t testCounter = 0; for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) { SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; - if (material->AlbedoTexture.Path && !material->AlbedoTexture.IsLoaded) + if (material->AlbedoTexture && !material->AlbedoTexture->IsLoaded) { - SampleLoadTextureData(loadDataCommandList, &material->AlbedoTexture, &applicationPayload->GpuMemory); + SampleLoadTextureData(loadDataCommandList, material->AlbedoTexture, &applicationPayload->GpuMemory); + testCounter++; } - if (material->NormalTexture.Path && !material->NormalTexture.IsLoaded) + if (material->NormalTexture && !material->NormalTexture->IsLoaded) { - SampleLoadTextureData(loadDataCommandList, &material->NormalTexture, &applicationPayload->GpuMemory); + SampleLoadTextureData(loadDataCommandList, material->NormalTexture, &applicationPayload->GpuMemory); + testCounter++; } } + // TODO: Measure scene loading time + if (testCounter > 0) + { + printf("TextureCount: %d\n", testCounter); + } + for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) { SampleSceneNodeHeader* sceneNode = &applicationPayload->TestSceneData.Nodes[i]; diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index 6cc7af8b..ab5c59ef 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -211,6 +211,10 @@ int main(int argc, const char* argv[]) { loadSceneOptions.Scaling = 0.01f; } + else if (strstr(inputPath, "Sponza.gltf")) + { + //loadSceneOptions.CoordinateSystem = ElemSceneCoordinateSystem_RightHanded; + } else if (strstr(inputPath, "bistro")) { //loadSceneOptions.CoordinateSystem = ElemSceneCoordinateSystem_RightHanded; diff --git a/samples/ElementalTools/03-TextureCompiler/main.c b/samples/ElementalTools/03-TextureCompiler/main.c index 797c01b7..49264a05 100644 --- a/samples/ElementalTools/03-TextureCompiler/main.c +++ b/samples/ElementalTools/03-TextureCompiler/main.c @@ -45,6 +45,12 @@ int main(int argc, const char* argv[]) ElemTextureMipDataSpan mipData = loadTextureResult.MipData; + if (mipData.Length == 0) + { + printf("No mip data in the image..."); + return 1; + } + if (mipData.Length == 1) { double beforeMipGenerationTimer = SampleGetTimerValueInMS(); @@ -66,6 +72,9 @@ int main(int argc, const char* argv[]) } printf("Writing texture data to: %s\n", outputPath); + + // TODO: Create the directory if it doesn't exist + FILE* file = fopen(outputPath, "wb"); SampleTextureHeader textureHeader = @@ -83,31 +92,47 @@ int main(int argc, const char* argv[]) SampleTextureDataBlockEntry* mipDataOffsets = (SampleTextureDataBlockEntry*)malloc(sizeof(SampleTextureDataBlockEntry) * mipData.Length); fwrite(mipDataOffsets, sizeof(SampleTextureDataBlockEntry), mipData.Length, file); - double beforeEncodeTimer = SampleGetTimerValueInMS(); - // TODO: Add format in the logging message - printf("Compressing data...\n"); - - for (uint32_t i = 0; i < mipData.Length; i++) + if (loadTextureResult.Format != ElemToolsGraphicsFormat_BC7) { - ElemTextureMipData* mipLevelData = &mipData.Items[i]; - printf("Mip level %d Size: %dx%d\n", i, mipLevelData->Width, mipLevelData->Height); + double beforeEncodeTimer = SampleGetTimerValueInMS(); + // TODO: Add format in the logging message + printf("Compressing data...\n"); - ElemCompressTextureMipDataResult compressedTextureData = ElemCompressTextureMipData(ElemToolsGraphicsFormat_BC7, mipLevelData, NULL); + for (uint32_t i = 0; i < mipData.Length; i++) + { + ElemTextureMipData* mipLevelData = &mipData.Items[i]; + printf("Mip level %d Size: %dx%d\n", i, mipLevelData->Width, mipLevelData->Height); - DisplayOutputMessages("CompressTextureMipData", compressedTextureData.Messages); + ElemCompressTextureMipDataResult compressedTextureData = ElemCompressTextureMipData(ElemToolsGraphicsFormat_BC7, mipLevelData, NULL); - if (compressedTextureData.HasErrors) - { - return 1; + DisplayOutputMessages("CompressTextureMipData", compressedTextureData.Messages); + + if (compressedTextureData.HasErrors) + { + return 1; + } + + uint32_t dataOffset = ftell(file); + fwrite(compressedTextureData.MipData.Data.Items, sizeof(uint8_t), compressedTextureData.MipData.Data.Length, file); + + mipDataOffsets[i] = (SampleTextureDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; } - - uint32_t dataOffset = ftell(file); - fwrite(compressedTextureData.MipData.Data.Items, sizeof(uint8_t), compressedTextureData.MipData.Data.Length, file); - mipDataOffsets[i] = (SampleTextureDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; + printf("Compressed data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); } + else + { + for (uint32_t i = 0; i < mipData.Length; i++) + { + ElemTextureMipData* mipLevelData = &mipData.Items[i]; + printf("Mip level %d Size: %dx%d\n", i, mipLevelData->Width, mipLevelData->Height); + + uint32_t dataOffset = ftell(file); + fwrite(mipLevelData->Data.Items, sizeof(uint8_t), mipLevelData->Data.Length, file); - printf("Compressed data in %.2fs\n", (SampleGetTimerValueInMS() - beforeEncodeTimer) / 1000.0); + mipDataOffsets[i] = (SampleTextureDataBlockEntry) { .Offset = dataOffset, .SizeInBytes = ftell(file) - dataOffset }; + } + } fseek(file, sizeof(SampleTextureHeader), SEEK_SET); fwrite(mipDataOffsets, sizeof(SampleTextureDataBlockEntry), mipData.Length, file); diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index e3ed30d6..7003877e 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -70,6 +70,9 @@ MTL::PixelFormat ConvertToMetalResourceFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return MTL::PixelFormatDepth32Float; + case ElemGraphicsFormat_BC7: + return MTL::PixelFormatBC7_RGBAUnorm; + case ElemGraphicsFormat_BC7_SRGB: return MTL::PixelFormatBC7_RGBAUnorm_sRGB; @@ -97,6 +100,9 @@ ElemGraphicsFormat ConvertFromMetalResourceFormat(MTL::PixelFormat format) case MTL::PixelFormatDepth32Float: return ElemGraphicsFormat_D32_FLOAT; + case MTL::PixelFormatBC7_RGBAUnorm: + return ElemGraphicsFormat_BC7; + case MTL::PixelFormatBC7_RGBAUnorm_sRGB: return ElemGraphicsFormat_BC7_SRGB; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index 7130abc9..a7f01aed 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -65,6 +65,9 @@ VkFormat ConvertToVulkanTextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return VK_FORMAT_D32_SFLOAT; + case ElemGraphicsFormat_BC7: + return VK_FORMAT_BC7_UNORM_BLOCK; + case ElemGraphicsFormat_BC7_SRGB: return VK_FORMAT_BC7_SRGB_BLOCK; diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 9f301b74..5f54f8b1 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -397,6 +397,7 @@ typedef enum ElemGraphicsFormat_R16G16B16A16_FLOAT, ElemGraphicsFormat_R32G32B32A32_FLOAT, ElemGraphicsFormat_D32_FLOAT, + ElemGraphicsFormat_BC7, ElemGraphicsFormat_BC7_SRGB } ElemGraphicsFormat; diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 038dc010..1adeaea2 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -193,6 +193,9 @@ DXGI_FORMAT ConvertToDirectX12TextureFormat(ElemGraphicsFormat format) case ElemGraphicsFormat_D32_FLOAT: return DXGI_FORMAT_D32_FLOAT; + case ElemGraphicsFormat_BC7: + return DXGI_FORMAT_BC7_UNORM; + case ElemGraphicsFormat_BC7_SRGB: return DXGI_FORMAT_BC7_UNORM_SRGB; @@ -220,6 +223,9 @@ ElemGraphicsFormat ConvertFromDirectX12TextureFormat(DXGI_FORMAT format) case DXGI_FORMAT_D32_FLOAT: return ElemGraphicsFormat_D32_FLOAT; + case DXGI_FORMAT_BC7_UNORM: + return ElemGraphicsFormat_BC7; + case DXGI_FORMAT_BC7_UNORM_SRGB: return ElemGraphicsFormat_BC7_SRGB; diff --git a/src/ElementalTools/ElementalTools.h b/src/ElementalTools/ElementalTools.h index 5257b31e..a5c9d851 100644 --- a/src/ElementalTools/ElementalTools.h +++ b/src/ElementalTools/ElementalTools.h @@ -404,6 +404,7 @@ ElemToolsAPI ElemBuildMeshletResult ElemBuildMeshlets(ElemVertexBuffer vertexBuf typedef enum { + ElemToolsGraphicsFormat_Unknown, ElemToolsGraphicsFormat_R8G8B8A8, ElemToolsGraphicsFormat_BC7 } ElemToolsGraphicsFormat; @@ -412,6 +413,9 @@ typedef enum { ElemLoadTextureFileFormat_Unknown = 0, ElemLoadTextureFileFormat_Tga = 1, + ElemLoadTextureFileFormat_Jpg = 2, + ElemLoadTextureFileFormat_Png = 3, + ElemLoadTextureFileFormat_Dds = 4, } ElemLoadTextureFileFormat; typedef struct diff --git a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h index 69e0ac66..cda785d5 100644 --- a/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Apple/PreCompiledHeader.h @@ -43,6 +43,8 @@ #include "stb_image.h" #include "stb_image_resize2.h" +#include "mikktspace.h" +#include "mikktspace.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h index 8428b8bc..542d9365 100644 --- a/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Linux/PreCompiledHeader.h @@ -42,6 +42,7 @@ #include "cgltf.h" #include "stb_image.h" #include "stb_image_resize2.h" +#include "mikktspace.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h index 6b7ba473..deaff135 100644 --- a/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h +++ b/src/ElementalTools/Platforms/Microsoft/PreCompiledHeader.h @@ -42,6 +42,7 @@ using namespace Microsoft::WRL; #include "cgltf.h" #include "stb_image.h" #include "stb_image_resize2.h" +#include "mikktspace.h" #ifdef _WASDEBUG #define _DEBUG diff --git a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp index 5a22a49b..ef84bb89 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderGltf.cpp @@ -32,8 +32,9 @@ ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_ // TODO: Tangent auto positionSize = sizeof(float) * 3; auto normalSize = sizeof(float) * 3; + auto tangentSize = sizeof(float) * 4; auto textureCoordinatesSize = sizeof(float) * 2; - auto maxVertexSize = positionSize + normalSize + textureCoordinatesSize; + auto maxVertexSize = positionSize + normalSize + tangentSize + textureCoordinatesSize; // TODO: Temporary auto realVertexSize = maxVertexSize; @@ -42,11 +43,13 @@ ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_ // TODO: Combine that with the components selection auto positionAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_position, 0); auto normalAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_normal, 0); + auto tangentAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_tangent, 0); auto textureCoordinatesAccessor = cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_texcoord, 0); // TODO: Rework this auto gltfPositionBuffer = SystemPushArray(stackMemoryArena, vertexCount * 3); auto gltfNormalBuffer = SystemPushArray(stackMemoryArena, vertexCount * 3); + auto gltfTangentBuffer = SystemPushArray(stackMemoryArena, vertexCount * 4); auto gltfTextureCoordinatesBuffer = SystemPushArray(stackMemoryArena, vertexCount * 2); SystemAssert(positionAccessor); @@ -57,6 +60,11 @@ ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_ cgltf_accessor_unpack_floats(normalAccessor, gltfNormalBuffer.Pointer, gltfNormalBuffer.Length); } + if (tangentAccessor) + { + cgltf_accessor_unpack_floats(tangentAccessor, gltfTangentBuffer.Pointer, gltfTangentBuffer.Length); + } + if (textureCoordinatesAccessor) { cgltf_accessor_unpack_floats(textureCoordinatesAccessor, gltfTextureCoordinatesBuffer.Pointer, gltfTextureCoordinatesBuffer.Length); @@ -71,6 +79,11 @@ ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_ for (uint32_t j = 0; j < 3; j++) { ((float*)currentVertexBufferPointer)[j] = gltfPositionBuffer[i * 3 + j]; + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } } AddPointToBoundingBox({ ((float*)currentVertexBufferPointer)[0], ((float*)currentVertexBufferPointer)[1], ((float*)currentVertexBufferPointer)[2]}, boundingBox); @@ -88,6 +101,19 @@ ElemVertexBuffer ConstructGltfVertexBuffer(MemoryArena memoryArena, const cgltf_ } currentVertexBufferPointer += 3 * sizeof(float); + + // Tangent + for (uint32_t j = 0; j < 4; j++) + { + ((float*)currentVertexBufferPointer)[j] = gltfTangentBuffer[i * 4 + j]; + + if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + ((float*)currentVertexBufferPointer)[2] = -((float*)currentVertexBufferPointer)[2]; + } + } + + currentVertexBufferPointer += 4 * sizeof(float); // Texture coordinates for (uint32_t j = 0; j < 2; j++) @@ -115,62 +141,19 @@ ElemUInt32Span ConstructGltfIndexBuffer(MemoryArena memoryArena, const cgltf_pri { for (uint32_t i = 0; i < indexBuffer.Length; i += 3) { - if (coordinateSystem == ElemSceneCoordinateSystem_LeftHanded) - { - uint32_t temp = indexBuffer[i + 1]; - indexBuffer[i + 1] = indexBuffer[i + 2]; - indexBuffer[i + 2] = temp; - } + uint32_t temp = indexBuffer[i + 1]; + indexBuffer[i + 1] = indexBuffer[i + 2]; + indexBuffer[i + 2] = temp; } } return { .Items = indexBuffer.Pointer, .Length = (uint32_t)indexBuffer.Length }; } -ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* options) +ReadOnlySpan LoadGltfMeshes(MemoryArena memoryArena, const cgltf_data* data, const ElemLoadSceneOptions* options, ToolsMessageList* messageList) { auto stackMemoryArena = SystemGetStackMemoryArena(); - auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); - - auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); - - auto messages = SystemPushArray(sceneLoaderMemoryArena, 1024); - auto messageCount = 0u; - auto hasErrors = false; - - cgltf_options cgltfOptions = - { - .file = - { - .read = ClgtfFileRead, - .release = ClgtfFileRelease - } - }; - - cgltf_data* data = NULL; - cgltf_result result = cgltf_parse_file(&cgltfOptions, path, &data); - - if (result != cgltf_result_success) - { - return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while reading glTF file."), .HasErrors = true }; - } - - result = cgltf_load_buffers(&cgltfOptions, data, path); - - if (result != cgltf_result_success) - { - return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while loading glTF buffers."), .HasErrors = true }; - } - - result = cgltf_validate(data); - - if (result != cgltf_result_success) - { - return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while validating glTF file."), .HasErrors = true }; - } - - auto meshes = SystemPushArray(sceneLoaderMemoryArena, data->meshes_count); - auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, data->nodes_count); + auto meshes = SystemPushArray(memoryArena, data->meshes_count); for (uint32_t i = 0; i < data->meshes_count; i++) { @@ -183,7 +166,7 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } }; - auto meshPrimitives = SystemPushArray(sceneLoaderMemoryArena, gltfMesh->primitives_count); + auto meshPrimitives = SystemPushArray(memoryArena, gltfMesh->primitives_count); for (uint32_t j = 0; j < gltfMesh->primitives_count; j++) { @@ -192,23 +175,13 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* if (gltfPrimitive->type != cgltf_primitive_type_triangles) { - messages[messageCount++] = - { - .Type = ElemToolsMessageType_Warning, - .Message = "glTF loader only support triangles for now." - }; - + WriteToMessageList(ElemToolsMessageType_Warning, "glTF loader only support triangles for now.", messageList); continue; } if (!cgltf_find_accessor(gltfPrimitive, cgltf_attribute_type_position, 0)) { - messages[messageCount++] = - { - .Type = ElemToolsMessageType_Error, - .Message = "The mesh primitive doesn't contains a position attribute." - }; - + WriteToMessageList(ElemToolsMessageType_Warning, "The mesh primitive doesn't contains a position attribute.", messageList); continue; } @@ -218,23 +191,119 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* .MaxPoint = { -FLT_MAX, -FLT_MAX, -FLT_MAX } }; - meshPrimitive->VertexBuffer = ConstructGltfVertexBuffer(sceneLoaderMemoryArena, gltfPrimitive, options->CoordinateSystem, &meshPrimitive->BoundingBox); - meshPrimitive->IndexBuffer = ConstructGltfIndexBuffer(sceneLoaderMemoryArena, gltfPrimitive, options->CoordinateSystem); + meshPrimitive->VertexBuffer = ConstructGltfVertexBuffer(memoryArena, gltfPrimitive, options->CoordinateSystem, &meshPrimitive->BoundingBox); + meshPrimitive->IndexBuffer = ConstructGltfIndexBuffer(memoryArena, gltfPrimitive, options->CoordinateSystem); + + meshPrimitive->MaterialId = gltfPrimitive->material ? cgltf_material_index(data, gltfPrimitive->material) : -1; AddBoundingBoxToBoundingBox(&meshPrimitive->BoundingBox, &mesh->BoundingBox); } - mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(gltfMesh->name)).Pointer; + if (gltfMesh->name) + { + mesh->Name = SystemDuplicateBuffer(memoryArena, ReadOnlySpan(gltfMesh->name)).Pointer; + } + else + { + mesh->Name = SystemConcatBuffers(memoryArena, "Mesh_", SystemConvertNumberToString(stackMemoryArena, i)).Pointer; + } + mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; } + return meshes; +} + +ReadOnlySpan LoadGltfMaterials(MemoryArena memoryArena, const cgltf_data* data, const ElemLoadSceneOptions* options, ToolsMessageList* messageList) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto materials = SystemPushArray(memoryArena, data->materials_count); + + for (uint32_t i = 0; i < data->materials_count; i++) + { + auto material = &materials[i]; + auto gltfMaterial = &data->materials[i]; + + if (gltfMaterial->name) + { + material->Name = SystemDuplicateBuffer(memoryArena, ReadOnlySpan(gltfMaterial->name)).Pointer; + } + else + { + material->Name = SystemConcatBuffers(memoryArena, "Material_", SystemConvertNumberToString(stackMemoryArena, i)).Pointer; + } + + if (gltfMaterial->has_pbr_metallic_roughness) + { + auto gltfMaterialData = &gltfMaterial->pbr_metallic_roughness; + + if (gltfMaterialData->base_color_texture.texture) + { + material->AlbedoTexturePath = SystemDuplicateBuffer(memoryArena, gltfMaterialData->base_color_texture.texture->image->uri).Pointer; + } + + material->AlbedoFactor = + { + .X = gltfMaterialData->base_color_factor[0], + .Y = gltfMaterialData->base_color_factor[1], + .Z = gltfMaterialData->base_color_factor[2], + .W = gltfMaterialData->base_color_factor[3] + }; + } + else if (gltfMaterial->has_pbr_specular_glossiness) + { + auto gltfMaterialData = &gltfMaterial->pbr_specular_glossiness; + + if (gltfMaterialData->diffuse_texture.texture) + { + material->AlbedoTexturePath = SystemDuplicateBuffer(memoryArena, gltfMaterialData->diffuse_texture.texture->image->uri).Pointer; + } + + material->AlbedoFactor = + { + .X = gltfMaterialData->diffuse_factor[0], + .Y = gltfMaterialData->diffuse_factor[1], + .Z = gltfMaterialData->diffuse_factor[2], + .W = gltfMaterialData->diffuse_factor[3] + }; + } + else + { + WriteToMessageList(ElemToolsMessageType_Warning, "glTF loader only support metallic roughness materials for now.", messageList); + } + + if (gltfMaterial->normal_texture.texture) + { + material->NormalTexturePath = SystemDuplicateBuffer(memoryArena, gltfMaterial->normal_texture.texture->image->uri).Pointer; + } + + printf("Material: %s\n", material->Name); + } + + return materials; +} + +ReadOnlySpan LoadGltfSceneNodes(MemoryArena memoryArena, const cgltf_data* data, const ElemLoadSceneOptions* options, ToolsMessageList* messageList) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto sceneNodes = SystemPushArray(memoryArena, data->nodes_count); + auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); + // TODO: Children? for (uint32_t i = 0; i < data->nodes_count; i++) { auto sceneNode = &sceneNodes[i]; auto gltfNode = &data->nodes[i]; - sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(gltfNode->name)).Pointer; + if (gltfNode->name) + { + sceneNode->Name = SystemDuplicateBuffer(memoryArena, ReadOnlySpan(gltfNode->name)).Pointer; + } + else + { + sceneNode->Name = SystemConcatBuffers(memoryArena, "Node_", SystemConvertNumberToString(stackMemoryArena, i)).Pointer; + } + sceneNode->Rotation = {}; sceneNode->Translation = {}; @@ -253,12 +322,60 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* auto flipMatrix = ElemToolsCreateIdentityMatrix(); flipMatrix.Rows[2].Z = -1; - nodeTransform = ElemToolsMulMatrix4x4(nodeTransform, flipMatrix); + nodeTransform = ElemToolsMulMatrix4x4(flipMatrix, ElemToolsMulMatrix4x4(nodeTransform, flipMatrix)); } nodeTransform = ElemToolsMulMatrix4x4(nodeTransform, globalTransformMatrix); DecomposeTransform(nodeTransform, &sceneNode->Scale, &sceneNode->Rotation, &sceneNode->Translation); } + + return sceneNodes; +} + +ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto sceneLoaderMemoryArena = GetSceneLoaderMemoryArena(); + + ToolsMessageList messageList = + { + .Messages = SystemPushArray(sceneLoaderMemoryArena, 1024) + }; + + cgltf_options cgltfOptions = + { + .file = + { + .read = ClgtfFileRead, + .release = ClgtfFileRelease + } + }; + + cgltf_data* data = NULL; + cgltf_result result = cgltf_parse_file(&cgltfOptions, path, &data); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while reading glTF file."), .HasErrors = true }; + } + + result = cgltf_load_buffers(&cgltfOptions, data, path); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while loading glTF buffers."), .HasErrors = true }; + } + + result = cgltf_validate(data); + + if (result != cgltf_result_success) + { + return { .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Error while validating glTF file."), .HasErrors = true }; + } + + auto meshes = LoadGltfMeshes(sceneLoaderMemoryArena, data, options, &messageList); + auto sceneNodes = LoadGltfSceneNodes(sceneLoaderMemoryArena, data, options, &messageList); + auto materials = LoadGltfMaterials(sceneLoaderMemoryArena, data, options, &messageList); cgltf_free(data); ResetLoadFileDataMemory(); @@ -267,9 +384,10 @@ ElemLoadSceneResult LoadGltfScene(const char* path, const ElemLoadSceneOptions* { .SceneFormat = ElemSceneFormat_Gltf, .CoordinateSystem = options->CoordinateSystem, - .Meshes = { .Items = meshes.Pointer, .Length = (uint32_t)meshes.Length }, - .Nodes = { .Items = sceneNodes.Pointer, .Length = (uint32_t)sceneNodes.Length }, - .Messages = { .Items = messages.Pointer, .Length = messageCount }, - .HasErrors = hasErrors + .Meshes = { .Items = (ElemSceneMesh*)meshes.Pointer, .Length = (uint32_t)meshes.Length }, + .Materials = { .Items = (ElemSceneMaterial*)materials.Pointer, .Length = (uint32_t)materials.Length }, + .Nodes = { .Items = (ElemSceneNode*)sceneNodes.Pointer, .Length = (uint32_t)sceneNodes.Length }, + .Messages = { .Items = messageList.Messages.Pointer, .Length = messageList.MessageCount }, + .HasErrors = messageList.HasErrors }; } diff --git a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp index 5989d8bd..d31f77b3 100644 --- a/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp +++ b/src/ElementalTools/SceneLoading/SceneLoaderObj.cpp @@ -1,5 +1,6 @@ #include "SceneLoader.h" #include "ToolsUtils.h" +#include "TangentSpaceGenerator.h" #include "ElementalTools.h" #include "SystemMemory.h" #include "SystemFunctions.h" @@ -12,8 +13,10 @@ struct ObjLoaderFileData struct ObjMeshPrimitiveInfo { - uint32_t IndexOffset; - uint32_t IndexCount; + uint32_t VertexOffset; + uint32_t VertexCount; + uint32_t FaceOffset; + uint32_t FaceCount; int32_t MaterialId; }; @@ -22,11 +25,13 @@ struct ObjVertex ElemToolsVector3 Position; ElemToolsVector3 Normal; ElemToolsVector4 Tangent; + ElemToolsVector3 Bitangent; ElemToolsVector2 TextureCoordinates; }; void* FastObjFileOpen(const char* path, void* userData) { + // TODO: Add support for streaming auto objFileData = LoadFileData(path); if (objFileData.Length == 0) @@ -55,7 +60,6 @@ size_t FastObjFileRead(void* file, void* destination, size_t bytes, void* userDa auto readLength = SystemMin(sourceSpan.Length - objLoaderFileData->CurrentOffset, bytes); - // TODO: For the map_Dist that isn't parsed by the lib, maybe we can replace it with bump here? if (readLength > 0) { SystemCopyBuffer(destinationSpan, sourceSpan.Slice(objLoaderFileData->CurrentOffset, readLength)); @@ -153,42 +157,6 @@ void WriteObjVertex(const ObjVertex* vertex, uint8_t** vertexBufferPointer) *vertexBufferPointer = currentVertexBufferPointer; } -struct ObjTriangle -{ - uint32_t Vertices[3]; - // TODO: More data -}; - -// TODO: Generalize this function so that it can be used by other loaders -ElemToolsVector3 ComputeTriangleTangentVector(const ObjTriangle* triangle, ReadOnlySpan vertexList) -{ - // TODO: Refactor that - ObjVertex triangleVertices[3]; - - for (uint32_t j = 0; j < 3; j++) - { - triangleVertices[j] = vertexList[triangle->Vertices[j]]; - } - - // TODO: Put that in a function because we may use it of GLTF too if the tangents are missing - // Compute tangents - auto edge1 = triangleVertices[1].Position - triangleVertices[0].Position; - auto edge2 = triangleVertices[2].Position - triangleVertices[0].Position; - - auto deltaUV1 = triangleVertices[1].TextureCoordinates - triangleVertices[0].TextureCoordinates; - auto deltaUV2 = triangleVertices[2].TextureCoordinates - triangleVertices[0].TextureCoordinates; - - float f = 1.0f / (deltaUV1.X * deltaUV2.Y - deltaUV2.X * deltaUV1.Y); - - ElemToolsVector3 tangent = {}; - - tangent.X = f * (deltaUV2.Y * edge1.X - deltaUV1.Y * edge2.X); - tangent.Y = f * (deltaUV2.Y * edge1.Y - deltaUV1.Y * edge2.Y); - tangent.Z = f * (deltaUV2.Y * edge1.Z - deltaUV1.Y * edge2.Z); - - return tangent; -} - ElemSceneMeshPrimitive ConstructObjMeshPrimitive(MemoryArena memoryArena, const fastObjMesh* objMesh, const ObjMeshPrimitiveInfo* meshPrimitiveInfo, const ElemLoadSceneOptions* options) { auto stackMemoryArena = SystemGetStackMemoryArena(); @@ -212,62 +180,100 @@ ElemSceneMeshPrimitive ConstructObjMeshPrimitive(MemoryArena memoryArena, const // TODO: Temporary auto realVertexSize = maxVertexSize; - auto indexBufferData = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount); + // TODO: We can just have one loop again, it is good that we have separated the read and write + auto vertexList = SystemPushArray(stackMemoryArena, meshPrimitiveInfo->VertexCount); - auto triangleCount = meshPrimitiveInfo->IndexCount / 3; - auto triangleList = SystemPushArray(stackMemoryArena, triangleCount); - auto vertexList = SystemPushArray(stackMemoryArena, 3 * triangleCount); - auto vertexCount = 0u; + for (uint32_t i = 0; i < meshPrimitiveInfo->VertexCount; i++) + { + // TODO: Here, we can directly write the vertex data to the buffer + vertexList[i] = ReadObjVertex(objMesh->indices[meshPrimitiveInfo->VertexOffset + i], objMesh, options->CoordinateSystem, options->FlipVerticalTextureCoordinates); + AddPointToBoundingBox(vertexList[i].Position, &result.BoundingBox); + } + + auto indexCount = 0u; + + for (uint32_t i = 0; i < meshPrimitiveInfo->FaceCount; i++) + { + if (objMesh->face_vertices[meshPrimitiveInfo->FaceOffset + i] == 3) + { + indexCount += 3; + } + else + { + indexCount += 6; + } + } + + printf("IndexCount: %d\n", indexCount); + auto indexBufferData = SystemPushArray(memoryArena, indexCount); + auto currentIndex = 0u; + auto currentObjIndex = 0u; - for (uint32_t i = 0; i < triangleCount; i++) + for (uint32_t i = 0; i < meshPrimitiveInfo->FaceCount; i++) { - auto triangle = &triangleList[i]; + auto faceVertexCount = objMesh->face_vertices[meshPrimitiveInfo->FaceOffset + i]; - for (uint32_t j = 0; j < 3; j++) + if (faceVertexCount == 3) { - auto vertex = ReadObjVertex(objMesh->indices[meshPrimitiveInfo->IndexOffset + i * 3 + j], objMesh, options->CoordinateSystem, options->FlipVerticalTextureCoordinates); + indexBufferData[currentIndex] = currentObjIndex; + indexBufferData[currentIndex + 1] = currentObjIndex + 1; + indexBufferData[currentIndex + 2] = currentObjIndex + 2; - // TODO: Check for duplicates - auto vertexIndex = vertexCount++; - vertexList[vertexIndex] = vertex; - AddPointToBoundingBox(vertex.Position, &result.BoundingBox); + if (options->CoordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + auto tmp = indexBufferData[currentIndex + 1]; + indexBufferData[currentIndex + 1] = indexBufferData[currentIndex + 2]; + indexBufferData[currentIndex + 2] = tmp; + } - triangle->Vertices[j] = vertexIndex; - indexBufferData[i * 3 + j] = vertexIndex; + currentIndex += 3; } - - if (options->CoordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + else { - auto tmp = indexBufferData[i * 3 + 1]; - indexBufferData[i * 3 + 1] = indexBufferData[i * 3 + 2]; - indexBufferData[i * 3 + 2] = tmp; - } + indexBufferData[currentIndex] = currentObjIndex; + indexBufferData[currentIndex + 1] = currentObjIndex + 1; + indexBufferData[currentIndex + 2] = currentObjIndex + 2; - auto triangleTangent = ComputeTriangleTangentVector(triangle, vertexList); + indexBufferData[currentIndex + 3] = currentObjIndex + 0; + indexBufferData[currentIndex + 4] = currentObjIndex + 2; + indexBufferData[currentIndex + 5] = currentObjIndex + 3; - for (uint32_t j = 0; j < 3; j++) - { - auto vertexIndex = triangle->Vertices[j]; - vertexList[vertexIndex].Tangent.XYZ = vertexList[vertexIndex].Tangent.XYZ + triangleTangent; + if (options->CoordinateSystem == ElemSceneCoordinateSystem_LeftHanded) + { + auto tmp = indexBufferData[currentIndex + 1]; + indexBufferData[currentIndex + 1] = indexBufferData[currentIndex + 2]; + indexBufferData[currentIndex + 2] = tmp; + + tmp = indexBufferData[currentIndex + 4]; + indexBufferData[currentIndex + 4] = indexBufferData[currentIndex + 5]; + indexBufferData[currentIndex + 5] = tmp; + } + + currentIndex += 6; } + + currentObjIndex += faceVertexCount; } - - auto vertexBufferData = SystemPushArray(memoryArena, meshPrimitiveInfo->IndexCount * maxVertexSize); - auto currentVertexBufferPointer = vertexBufferData.Pointer; - for (uint32_t i = 0; i < vertexCount; i++) + // TODO: To replace + GenerateTangentVectorsParameters generateTangentParams = { - auto normal = vertexList[i].Normal; - auto tangent = ElemToolsNormalizeV3(vertexList[i].Tangent.XYZ); + .PositionPointer = &vertexList.Pointer[0].Position, + .NormalPointer = &vertexList.Pointer[0].Normal, + .TextureCoordinatesPointer = &vertexList.Pointer[0].TextureCoordinates, + .TangentPointer = &vertexList.Pointer[0].Tangent, + .VertexSize = sizeof(ObjVertex), + .IndexData = indexBufferData + }; - // TODO: Active that when we have unique vertices - //tangent = ElemToolsNormalizeV3(tangent - normal * ElemToolsDotProductV3(normal, tangent)); + AssertIfFailed(GenerateTangentVectors(&generateTangentParams)); - // TODO: Process vertex data - vertexList[i].Tangent.XYZ = tangent; - vertexList[i].Tangent.W = 1.0f; - //float handedness = (glm::dot(glm::cross(normal, tangent), B) < 0.0f) ? -1.0f : 1.0f; + auto vertexBufferData = SystemPushArray(memoryArena, meshPrimitiveInfo->VertexCount * maxVertexSize); + auto currentVertexBufferPointer = vertexBufferData.Pointer; + // TODO: Remove that loop + for (uint32_t i = 0; i < meshPrimitiveInfo->VertexCount; i++) + { WriteObjVertex(&vertexList[i], ¤tVertexBufferPointer); } @@ -275,7 +281,7 @@ ElemSceneMeshPrimitive ConstructObjMeshPrimitive(MemoryArena memoryArena, const { .Data = { .Items = vertexBufferData.Pointer, .Length = (uint32_t)vertexBufferData.Length }, .VertexSize = (uint32_t)realVertexSize, - .VertexCount = meshPrimitiveInfo->IndexCount + .VertexCount = meshPrimitiveInfo->VertexCount }; result.IndexBuffer = { .Items = indexBufferData.Pointer, .Length = (uint32_t)indexBufferData.Length }; @@ -308,52 +314,50 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E auto globalTransformMatrix = CreateSceneLoaderGlobalTransformMatrix(options); - auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); - auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, objFileData->object_count); + auto meshes = SystemPushArray(sceneLoaderMemoryArena, objFileData->group_count); + auto sceneNodes = SystemPushArray(sceneLoaderMemoryArena, objFileData->group_count); auto meshPrimitiveInfos = SystemPushArray(stackMemoryArena, UINT16_MAX); - for (uint32_t i = 0; i < objFileData->object_count; i++) + for (uint32_t i = 0; i < objFileData->group_count; i++) { auto mesh = &meshes[i]; auto sceneNode = &sceneNodes[i]; - auto objectData = &objFileData->objects[i]; + auto groupData = &objFileData->groups[i]; - if (objectData->face_count == 0) + if (groupData->face_count == 0) { printf("Invalid object\n"); continue; } - auto currentFaceStart = objectData->face_offset; - auto currentFaceEnd = objectData->face_offset + objectData->face_count; - - auto indexOffset = 0u; + auto vertexOffset = 0u; - for (uint32_t j = 0; j < currentFaceStart; j++) + for (uint32_t j = 0; j < groupData->face_offset; j++) { - indexOffset += objFileData->face_vertices[j]; + vertexOffset += objFileData->face_vertices[j]; } - auto currentMaterial = objFileData->face_materials[currentFaceStart]; + auto currentMaterial = objFileData->face_materials[groupData->face_offset]; auto meshPrimitiveCount = 0u; auto meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; *meshPrimitiveInfo = { - .IndexOffset = indexOffset, + .VertexOffset = vertexOffset, + .FaceOffset = groupData->face_offset, .MaterialId = (int32_t)currentMaterial }; - for (uint32_t j = currentFaceStart; j < currentFaceEnd; j++) + for (uint32_t j = 0; j < groupData->face_count; j++) { - auto faceMaterial = objFileData->face_materials[j]; - int faceVertexCount = objFileData->face_vertices[j]; + auto faceMaterial = objFileData->face_materials[groupData->face_offset + j]; + auto faceVertexCount = objFileData->face_vertices[groupData->face_offset + j]; - if (faceVertexCount != 3) + if (faceVertexCount != 3 && faceVertexCount != 4) { return { - .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Obj mesh loader only support triangles."), + .Messages = ConstructErrorMessageSpan(sceneLoaderMemoryArena, "Obj mesh loader only support triangles or quads."), .HasErrors = true }; } @@ -363,17 +367,22 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E currentMaterial = faceMaterial; printf("Material: %d\n", currentMaterial); - auto meshPrimitiveIndexOffset = indexOffset + meshPrimitiveInfo->IndexCount; + auto previousVertexOffset = meshPrimitiveInfo->VertexOffset; + auto previousVertexCount = meshPrimitiveInfo->VertexCount; + auto previousFaceOffset = meshPrimitiveInfo->FaceOffset; + auto previousFaceCount = meshPrimitiveInfo->FaceCount; meshPrimitiveInfo = &meshPrimitiveInfos[meshPrimitiveCount++]; *meshPrimitiveInfo = { - .IndexOffset = meshPrimitiveIndexOffset, + .VertexOffset = previousVertexOffset + previousVertexCount, + .FaceOffset = previousFaceOffset + previousFaceCount, .MaterialId = (int32_t)currentMaterial }; } - meshPrimitiveInfo->IndexCount += faceVertexCount; + meshPrimitiveInfo->VertexCount += faceVertexCount; + meshPrimitiveInfo->FaceCount++; } mesh->BoundingBox = @@ -401,9 +410,9 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E ApplyObjBoundingBoxInverseTranslation(boundingBoxCenter, &meshPrimitive->BoundingBox); } - if (objectData->name) + if (groupData->name) { - mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + mesh->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(groupData->name)).Pointer; } else { @@ -412,9 +421,9 @@ ElemLoadSceneResult LoadObjSceneAndNodes(const fastObjMesh* objFileData, const E mesh->MeshPrimitives = { .Items = meshPrimitives.Pointer, .Length = (uint32_t)meshPrimitives.Length }; - if (objectData->name) + if (groupData->name) { - sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(objectData->name)).Pointer; + sceneNode->Name = SystemDuplicateBuffer(sceneLoaderMemoryArena, ReadOnlySpan(groupData->name)).Pointer; } else { diff --git a/src/ElementalTools/SceneLoading/TangentSpaceGenerator.cpp b/src/ElementalTools/SceneLoading/TangentSpaceGenerator.cpp new file mode 100644 index 00000000..956cb8d8 --- /dev/null +++ b/src/ElementalTools/SceneLoading/TangentSpaceGenerator.cpp @@ -0,0 +1,81 @@ +#include "TangentSpaceGenerator.h" + +int32_t MikkTSpaceGetNumFaces(const SMikkTSpaceContext* context) +{ + auto parameters = (GenerateTangentVectorsParameters*)context->m_pUserData; + return parameters->IndexData.Length / 3; +} + +int32_t MikkTSpaceGetNumVerticesOfFace(const SMikkTSpaceContext* context, int32_t face) +{ + return 3; +} + +void MikkTSpaceGetPosition(const SMikkTSpaceContext* context, float* outputPosition, int32_t faceIndex, int32_t vertexIndex) +{ + auto parameters = (GenerateTangentVectorsParameters*)context->m_pUserData; + + auto vertexGlobalIndex = parameters->IndexData[faceIndex * 3 + vertexIndex]; + auto position = (ElemToolsVector3*)((uint8_t*)parameters->PositionPointer + vertexGlobalIndex * parameters->VertexSize); + + outputPosition[0] = position->X; + outputPosition[1] = position->Y; + outputPosition[2] = position->Z; +} + +void MikkTSpaceGetNormal(const SMikkTSpaceContext* context, float* outputNormal, int32_t faceIndex, int32_t vertexIndex) +{ + auto parameters = (GenerateTangentVectorsParameters*)context->m_pUserData; + + auto vertexGlobalIndex = parameters->IndexData[faceIndex * 3 + vertexIndex]; + auto normal = (ElemToolsVector3*)((uint8_t*)parameters->NormalPointer + vertexGlobalIndex * parameters->VertexSize); + + outputNormal[0] = normal->X; + outputNormal[1] = normal->Y; + outputNormal[2] = normal->Z; +} + +void MikkTSpaceGetTextureCoordinates(const SMikkTSpaceContext* context, float* outputTextureCoordinates, int32_t faceIndex, int32_t vertexIndex) +{ + auto parameters = (GenerateTangentVectorsParameters*)context->m_pUserData; + + auto vertexGlobalIndex = parameters->IndexData[faceIndex * 3 + vertexIndex]; + auto textureCoordinates = (ElemToolsVector2*)((uint8_t*)parameters->TextureCoordinatesPointer + vertexGlobalIndex * parameters->VertexSize); + + outputTextureCoordinates[0] = textureCoordinates->X; + outputTextureCoordinates[1] = textureCoordinates->Y; +} + +void MikkTSpaceSetTangent(const SMikkTSpaceContext* context, const float* tangent, float sign, int32_t faceIndex, int32_t vertexIndex) +{ + auto parameters = (GenerateTangentVectorsParameters*)context->m_pUserData; + + auto vertexGlobalIndex = parameters->IndexData[faceIndex * 3 + vertexIndex]; + auto outputTangent = (ElemToolsVector4*)((uint8_t*)parameters->TangentPointer + vertexGlobalIndex * parameters->VertexSize); + + outputTangent->X = tangent[0]; + outputTangent->Y = tangent[1]; + outputTangent->Z = tangent[2]; + outputTangent->W = sign; +} + +bool GenerateTangentVectors(const GenerateTangentVectorsParameters* parameters) +{ + SMikkTSpaceInterface mikkTSpaceInterface + { + .m_getNumFaces = MikkTSpaceGetNumFaces, + .m_getNumVerticesOfFace = MikkTSpaceGetNumVerticesOfFace, + .m_getPosition = MikkTSpaceGetPosition, + .m_getNormal = MikkTSpaceGetNormal, + .m_getTexCoord = MikkTSpaceGetTextureCoordinates, + .m_setTSpaceBasic = MikkTSpaceSetTangent + }; + + SMikkTSpaceContext mikkTSpaceContext + { + .m_pInterface = &mikkTSpaceInterface, + .m_pUserData = (void*)parameters + }; + + return genTangSpaceDefault(&mikkTSpaceContext); +} diff --git a/src/ElementalTools/SceneLoading/TangentSpaceGenerator.h b/src/ElementalTools/SceneLoading/TangentSpaceGenerator.h new file mode 100644 index 00000000..dc18b079 --- /dev/null +++ b/src/ElementalTools/SceneLoading/TangentSpaceGenerator.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ElementalTools.h" +#include "SystemMemory.h" + +struct GenerateTangentVectorsParameters +{ + ElemToolsVector3* PositionPointer; + ElemToolsVector3* NormalPointer; + ElemToolsVector2* TextureCoordinatesPointer; + ElemToolsVector4* TangentPointer; + uint32_t VertexSize; + ReadOnlySpan IndexData; +}; + +bool GenerateTangentVectors(const GenerateTangentVectorsParameters* parameters); diff --git a/src/ElementalTools/Textures/TextureLoader.cpp b/src/ElementalTools/Textures/TextureLoader.cpp index 67eb9b18..99e5aba1 100644 --- a/src/ElementalTools/Textures/TextureLoader.cpp +++ b/src/ElementalTools/Textures/TextureLoader.cpp @@ -1,5 +1,7 @@ #include "TextureLoader.h" +#include "SystemFunctions.h" #include "TextureLoaderStb.cpp" +#include "TextureLoaderDds.cpp" // TODO: Do one for each thread static MemoryArena TextureLoaderMemoryArena; @@ -34,6 +36,18 @@ ElemLoadTextureFileFormat GetLoadTextureFileFormatFromPath(const char* path) { return ElemLoadTextureFileFormat_Tga; } + else if (SystemFindSubString(extension, "jpg") != -1) + { + return ElemLoadTextureFileFormat_Jpg; + } + else if (SystemFindSubString(extension, "png") != -1) + { + return ElemLoadTextureFileFormat_Png; + } + else if (SystemFindSubString(extension, "dds") != -1) + { + return ElemLoadTextureFileFormat_Dds; + } return ElemLoadTextureFileFormat_Unknown; } @@ -55,8 +69,13 @@ ElemToolsAPI ElemLoadTextureResult ElemLoadTexture(const char* path, const ElemL switch (textureFileFormat) { case ElemLoadTextureFileFormat_Tga: + case ElemLoadTextureFileFormat_Jpg: + case ElemLoadTextureFileFormat_Png: return LoadStbTexture(path, textureFileFormat, &loadTextureOptions); + case ElemLoadTextureFileFormat_Dds: + return LoadDdsTexture(path, textureFileFormat, &loadTextureOptions); + default: return { diff --git a/src/ElementalTools/Textures/TextureLoaderDds.cpp b/src/ElementalTools/Textures/TextureLoaderDds.cpp new file mode 100644 index 00000000..ac08418c --- /dev/null +++ b/src/ElementalTools/Textures/TextureLoaderDds.cpp @@ -0,0 +1,190 @@ +#include "TextureLoader.h" +#include "ToolsUtils.h" +#include "ElementalTools.h" +#include "SystemMemory.h" +#include "SystemFunctions.h" + +#define DDSCAPS2_CUBEMAP 0x200 +#define DDSCAPS2_VOLUME 0x200000 +#define DDS_DIMENSION_TEXTURE2D 3 + +struct DdsPixelFormat +{ + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwFourCC; + unsigned int dwRGBBitCount; + unsigned int dwRBitMask; + unsigned int dwGBitMask; + unsigned int dwBBitMask; + unsigned int dwABitMask; +}; + +struct DdsHeader +{ + unsigned int dwSize; + unsigned int dwFlags; + unsigned int dwHeight; + unsigned int dwWidth; + unsigned int dwPitchOrLinearSize; + unsigned int dwDepth; + unsigned int dwMipMapCount; + unsigned int dwReserved1[11]; + DdsPixelFormat ddspf; + unsigned int dwCaps; + unsigned int dwCaps2; + unsigned int dwCaps3; + unsigned int dwCaps4; + unsigned int dwReserved2; +}; + +struct DdsHeaderDirectX10 +{ + unsigned int dxgiFormat; + unsigned int resourceDimension; + unsigned int miscFlag; + unsigned int arraySize; + unsigned int miscFlags2; +}; + +enum DXGI_FORMAT +{ + DXGI_FORMAT_BC1_UNORM = 71, + DXGI_FORMAT_BC1_UNORM_SRGB = 72, + DXGI_FORMAT_BC2_UNORM = 74, + DXGI_FORMAT_BC2_UNORM_SRGB = 75, + DXGI_FORMAT_BC3_UNORM = 77, + DXGI_FORMAT_BC3_UNORM_SRGB = 78, + DXGI_FORMAT_BC4_UNORM = 80, + DXGI_FORMAT_BC4_SNORM = 81, + DXGI_FORMAT_BC5_UNORM = 83, + DXGI_FORMAT_BC5_SNORM = 84, + DXGI_FORMAT_BC6H_UF16 = 95, + DXGI_FORMAT_BC6H_SF16 = 96, + DXGI_FORMAT_BC7_UNORM = 98, + DXGI_FORMAT_BC7_UNORM_SRGB = 99, +}; + +uint32_t FourCC(const char value[5]) +{ + return ((uint32_t)value[0] << 0) | ((uint32_t)value[1] << 8) | ((uint32_t)value[2] << 16) | ((uint32_t)value[3] << 24); +} + +ElemToolsGraphicsFormat GetDdsTextureFormat(const DdsHeader* ddsHeader, const DdsHeaderDirectX10* directX10Header) +{ + // TODO: Other formats + if (ddsHeader->ddspf.dwFourCC == FourCC("DX10")) + { + if (directX10Header->dxgiFormat == DXGI_FORMAT_BC7_UNORM_SRGB || directX10Header->dxgiFormat == DXGI_FORMAT_BC7_UNORM) + { + return ElemToolsGraphicsFormat_BC7; + } + } + + return ElemToolsGraphicsFormat_Unknown; +} + +uint32_t GetDdsFormatBlockSize(ElemToolsGraphicsFormat format) +{ + // TODO: other formats + if (format == ElemToolsGraphicsFormat_BC7) + { + return 16; + } + + return 8; +} + +ElemLoadTextureResult LoadDdsTexture(const char* path, ElemLoadTextureFileFormat fileFormat, const ElemLoadTextureOptions* options) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto textureLoaderMemoryArena = GetTextureLoaderMemoryArena(); + + auto messages = SystemPushArray(textureLoaderMemoryArena, 1024); + auto messageCount = 0u; + auto hasErrors = false; + + auto objFileData = LoadFileData(path); + + if (objFileData.Length == 0) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Error while reading texture file."), .HasErrors = true }; + } + + auto currentFilePointer = objFileData.Pointer; + + if (*(uint32_t*)currentFilePointer != FourCC("DDS ")) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Texture is not a DDS file."), .HasErrors = true }; + } + + currentFilePointer += 4; + + auto header = (DdsHeader*)currentFilePointer; + currentFilePointer += sizeof(DdsHeader); + + DdsHeaderDirectX10* directX10Header = nullptr; + + if (header->ddspf.dwFourCC == FourCC("DX10")) + { + directX10Header = (DdsHeaderDirectX10*)currentFilePointer; + currentFilePointer += sizeof(DdsHeaderDirectX10); + } + + if (header->dwCaps2 & (DDSCAPS2_CUBEMAP | DDSCAPS2_VOLUME)) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Cubemaps and volume textures are not supported."), .HasErrors = true }; + } + + if (header->ddspf.dwFourCC == FourCC("DX10") && directX10Header->resourceDimension != DDS_DIMENSION_TEXTURE2D) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Only Texture2D are supported."), .HasErrors = true }; + } + + auto format = GetDdsTextureFormat(header, directX10Header); + + if (format == ElemToolsGraphicsFormat_Unknown) + { + return { .Messages = ConstructErrorMessageSpan(textureLoaderMemoryArena, "Unknown texture format."), .HasErrors = true }; + } + + auto width = header->dwWidth; + auto height = header->dwHeight; + auto mipLevels = header->dwMipMapCount; + auto blockSize = GetDdsFormatBlockSize(format); + + auto mipData = SystemPushArray(textureLoaderMemoryArena, mipLevels); + + for (uint32_t i = 0; i < mipLevels; i++) + { + auto mipLevelData = &mipData[i]; + printf("Processing mip level %d\n", i); + + auto mipWidth = SystemMax(1u, width >> i); + auto mipHeight = SystemMax(1u, height >> i); + + auto dataSizeInBytes = ((mipWidth + 3) / 4) * ((mipHeight + 3) / 4) * blockSize; + printf("Data size: %d\n", dataSizeInBytes); + + mipLevelData->Width = mipWidth; + mipLevelData->Height = mipHeight; + + auto mipLevelRawData = SystemDuplicateBuffer(textureLoaderMemoryArena, ReadOnlySpan((uint8_t*)currentFilePointer, dataSizeInBytes)); + mipLevelData->Data = { .Items = mipLevelRawData.Pointer, .Length = (uint32_t)mipLevelRawData.Length }; + + currentFilePointer += dataSizeInBytes; + } + + ResetLoadFileDataMemory(); + + return + { + .FileFormat = fileFormat, + .Format = format, + .Width = (uint32_t)width, + .Height = (uint32_t)height, + .MipData = { .Items = mipData.Pointer, .Length = (uint32_t)mipData.Length }, + .Messages = { .Items = messages.Pointer, .Length = messageCount }, + .HasErrors = hasErrors + }; +} diff --git a/src/ElementalTools/Textures/TextureLoaderStb.cpp b/src/ElementalTools/Textures/TextureLoaderStb.cpp index 8ff639bb..d69d8898 100644 --- a/src/ElementalTools/Textures/TextureLoaderStb.cpp +++ b/src/ElementalTools/Textures/TextureLoaderStb.cpp @@ -2,7 +2,6 @@ #include "ToolsUtils.h" #include "ElementalTools.h" #include "SystemMemory.h" -#include "SystemFunctions.h" ElemLoadTextureResult LoadStbTexture(const char* path, ElemLoadTextureFileFormat fileFormat, const ElemLoadTextureOptions* options) { diff --git a/src/ElementalTools/ToolsUtils.cpp b/src/ElementalTools/ToolsUtils.cpp index fb6d2ee5..d669b3a0 100644 --- a/src/ElementalTools/ToolsUtils.cpp +++ b/src/ElementalTools/ToolsUtils.cpp @@ -29,7 +29,7 @@ void InitStorageMemoryArena() { if (FileIOMemoryArena.Storage == nullptr) { - FileIOMemoryArena = SystemAllocateMemoryArena(512 * 1024 * 1024); + FileIOMemoryArena = SystemAllocateMemoryArena(2u * 1024u * 1024u * 1024u); } } @@ -46,6 +46,20 @@ void ResetLoadFileDataMemory() SystemClearMemoryArena(FileIOMemoryArena); } +void WriteToMessageList(ElemToolsMessageType type, const char* message, ToolsMessageList* messageList) +{ + messageList->Messages[messageList->MessageCount++] = + { + .Type = type, + .Message = message + }; + + if (type == ElemToolsMessageType_Error) + { + messageList->HasErrors = true; + } +} + ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const char* errorMessage) { auto messageItem = SystemPushStruct(memoryArena); diff --git a/src/ElementalTools/ToolsUtils.h b/src/ElementalTools/ToolsUtils.h index b644d73f..a52fd907 100644 --- a/src/ElementalTools/ToolsUtils.h +++ b/src/ElementalTools/ToolsUtils.h @@ -4,9 +4,18 @@ #include "SystemMemory.h" #include "SystemSpan.h" +struct ToolsMessageList +{ + Span Messages; + uint32_t MessageCount; + bool HasErrors; +}; + ReadOnlySpan LoadFileData(const char* path); void ResetLoadFileDataMemory(); +void WriteToMessageList(ElemToolsMessageType type, const char* message, ToolsMessageList* messageList); + ElemToolsMessageSpan ConstructErrorMessageSpan(MemoryArena memoryArena, const char* errorMessage); void AddPointToBoundingBox(ElemToolsVector3 point, ElemToolsBoundingBox* boundingBox); @@ -19,6 +28,7 @@ float ElemToolsDotProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2); float ElemToolsMagnitudeSquaredV3(ElemToolsVector3 v); float ElemToolsMagnitudeV3(ElemToolsVector3 v); ElemToolsVector3 ElemToolsNormalizeV3(ElemToolsVector3 v); +ElemToolsVector3 ElemToolsCrossProductV3(ElemToolsVector3 v1, ElemToolsVector3 v2); ElemToolsVector2 operator +(const ElemToolsVector2& v1, const ElemToolsVector2& v2); ElemToolsVector2 operator -(const ElemToolsVector2& v1, const ElemToolsVector2& v2); diff --git a/src/ElementalTools/UnityBuild.cpp b/src/ElementalTools/UnityBuild.cpp index 7914980a..a93bc425 100644 --- a/src/ElementalTools/UnityBuild.cpp +++ b/src/ElementalTools/UnityBuild.cpp @@ -7,6 +7,8 @@ #include "Shaders/MetalShaderConverter.cpp" #endif +#include "SceneLoading/TangentSpaceGenerator.cpp" + #define CGLTF_IMPLEMENTATION #include "cgltf.h" #include "fast_obj.c" @@ -27,6 +29,11 @@ #include "stb_image_resize2.h" #pragma clang diagnostic pop +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-but-set-variable" +#include "mikktspace.c" +#pragma clang diagnostic pop + #include "bc7enc.cpp" #include "Textures/TextureLoader.cpp" diff --git a/utilities/cmake/AppPackaging.cmake b/utilities/cmake/AppPackaging.cmake index c89c5cda..baa0a5bb 100644 --- a/utilities/cmake/AppPackaging.cmake +++ b/utilities/cmake/AppPackaging.cmake @@ -154,6 +154,9 @@ function(configure_resource_compilation target_name resource_list) "MESH|SceneCompiler|.obj|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" "MESH|SceneCompiler|.gltf|.scene|${MESH_COMPILER_DEFAULT_OPTIONS}" "TEXTURE|TextureCompiler|.tga|.texture|${TEXTURE_COMPILER_DEFAULT_OPTIONS}" + "TEXTURE|TextureCompiler|.jpg|.texture|${TEXTURE_COMPILER_DEFAULT_OPTIONS}" + "TEXTURE|TextureCompiler|.png|.texture|${TEXTURE_COMPILER_DEFAULT_OPTIONS}" + "TEXTURE|TextureCompiler|.dds|.texture|${TEXTURE_COMPILER_DEFAULT_OPTIONS}" ) set(all_compiled_resources "") From a13c1def17e9e2c00c2f28a3098d74c6842c5280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 15 Jan 2025 05:27:37 +0100 Subject: [PATCH 86/93] Fixes --- samples/Common/SampleSceneLoader.h | 8 ++++++-- samples/Elemental/99-Renderer/Data/RenderMesh.hlsl | 4 ++-- samples/ElementalTools/02-SceneCompiler/main.c | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/samples/Common/SampleSceneLoader.h b/samples/Common/SampleSceneLoader.h index 2f988aaa..5b1ebf46 100644 --- a/samples/Common/SampleSceneLoader.h +++ b/samples/Common/SampleSceneLoader.h @@ -121,6 +121,8 @@ void SampleLoadTexture(const char* path, SampleTextureData** textureDataPointer, // TODO: Here we need to use a global list of texture that are unique based on the path // For now the same texture is loaded multiple time which is really bad + // TODO: Don't do that, we can pre compute the list of unique textures in the compiler + // and replace the reference in each materials for (uint32_t i = 0; i < CurrentTextureCacheIndex; i++) { SampleTextureData* cacheTexture = &TextureCache[i]; @@ -255,14 +257,16 @@ void SampleLoadMaterial(const SampleSceneMaterialHeader* materialHeader, SampleM void SampleFreeMaterial(SampleMaterialData* materialData) { - if (materialData->AlbedoTexture->GpuTexture.Texture) + if (materialData->AlbedoTexture && materialData->AlbedoTexture->IsLoaded) { SampleFreeTexture(materialData->AlbedoTexture); + materialData->AlbedoTexture->IsLoaded = false; } - if (materialData->NormalTexture->GpuTexture.Texture) + if (materialData->NormalTexture && materialData->NormalTexture->IsLoaded) { SampleFreeTexture(materialData->NormalTexture); + materialData->NormalTexture->IsLoaded = false; } } diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 9f0c7539..230dd150 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -174,8 +174,8 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 //worldNormal = ResolveNormalFromSurfaceGradient(worldNormal, surfaceGradient); } - float nDotL = dot(worldNormal, normalize(float3(-0.5, 1, -0.5))); - float ambient = 0.6; + float nDotL = max(dot(worldNormal, normalize(float3(-1.0, 1.0, 1.0))), 0.0); + float ambient = 0.05; return albedo * (nDotL + ambient); return float4(worldNormal * 0.5 + 0.5, 1.0); diff --git a/samples/ElementalTools/02-SceneCompiler/main.c b/samples/ElementalTools/02-SceneCompiler/main.c index ab5c59ef..6e5e62b7 100644 --- a/samples/ElementalTools/02-SceneCompiler/main.c +++ b/samples/ElementalTools/02-SceneCompiler/main.c @@ -97,6 +97,8 @@ bool WriteSceneData(FILE* file, ElemLoadSceneResult scene, const char* sceneInpu strncpy(materialHeader.Name, material->Name, 50); + // TODO: Here we need to compute a list of unique textures and add them to a list + // so we don't need to compute that unique list at runtime if (material->AlbedoTexturePath) { GetRelativeResourcePath(sceneInputPath, material->AlbedoTexturePath, ".texture", materialHeader.AlbedoTexturePath, 255); From 238c4615cc31c42b585f44f1e066ab093757b04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 15 Jan 2025 07:28:16 +0100 Subject: [PATCH 87/93] WIP Apple resources --- src/Elemental/Apple/Graphics/MetalCommandList.cpp | 8 +++++++- src/Elemental/Apple/Graphics/MetalCommandList.h | 2 ++ src/Elemental/Apple/Graphics/MetalResource.cpp | 3 ++- tests/GraphicsTests/ResourceIOTests.cpp | 2 ++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index ee24446e..ddbc72e1 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -233,6 +233,11 @@ ElemFence MetalExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLis { fence = CreateMetalCommandQueueFence(commandQueue, commandListData->DeviceObject.get()); } + + for (uint32_t j = 0; j < commandListData->UploadBufferCount; j++) + { + UpdateUploadBufferPoolItemFence(commandListData->UploadBufferPoolItems[j], fence); + } commandListData->DeviceObject->commit(); commandListData->DeviceObject.reset(); @@ -265,7 +270,8 @@ void MetalWaitForFenceOnCpu(ElemFence fence) if (fence.FenceValue > commandQueueToWaitDataFull->LastCompletedFenceValue) { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for Fence on CPU..."); + // TODO: Log this with a special option? + //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for Fence on CPU..."); auto dispatchGroup = dispatch_group_create(); dispatch_group_enter(dispatchGroup); diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.h b/src/Elemental/Apple/Graphics/MetalCommandList.h index f12b6327..7ca5a030 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.h +++ b/src/Elemental/Apple/Graphics/MetalCommandList.h @@ -46,6 +46,8 @@ struct MetalCommandListData bool IsCommitted; ResourceBarrierPool ResourceBarrierPool; bool ArgumentBufferBound; + UploadBufferPoolItem>* UploadBufferPoolItems[MAX_UPLOAD_BUFFERS]; + uint32_t UploadBufferCount; }; struct MetalCommandListDataFull diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index e3ed30d6..02ac4ead 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -550,6 +550,7 @@ UploadBufferMemory> GetMetalUploadBuffer(ElemGraphics if (uploadBuffer.PoolItem->Fence.FenceValue > 0) { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Fence: %d", uploadBuffer.PoolItem->Fence.FenceValue); MetalWaitForFenceOnCpu(uploadBuffer.PoolItem->Fence); } @@ -608,7 +609,7 @@ void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopy if (resourceData->Format == ElemGraphicsFormat_BC7_SRGB) { uploadBufferAlignment = 16u; - uploadBufferSizeInBytes = SystemAlignToPowerOf2(uploadBufferSizeInBytes, 16u); + uploadBufferSizeInBytes = SystemAlign(uploadBufferSizeInBytes, 16u); } } diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index 03b74ffa..d7749098 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -403,6 +403,8 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandList ElemCopyDataToGraphicsResource(commandList, ¶meters); ElemCommitCommandList(commandList); fence = ElemExecuteCommandList(commandQueue, commandList, nullptr); + + ElemWaitForFenceOnCpu(fence); } // Assert From 6d55cc99c47cece61bceb0c4e4925ac94f3666dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 15 Jan 2025 15:11:55 +0100 Subject: [PATCH 88/93] Implement Metal samplers --- .github/workflows/run-tests.yml | 6 +- samples/Elemental/99-Renderer/main.c | 2 + .../Apple/Graphics/MetalCommandList.cpp | 2 +- src/Elemental/Apple/Graphics/MetalConfig.h | 3 +- .../Apple/Graphics/MetalGraphicsDevice.cpp | 67 +++++++++- .../Apple/Graphics/MetalGraphicsDevice.h | 4 + .../Apple/Graphics/MetalResource.cpp | 126 +++++++++++++++++- src/Elemental/Apple/Graphics/MetalResource.h | 6 + src/Elemental/Apple/Graphics/MetalShader.cpp | 33 +---- .../Shaders/MetalShaderConverter.cpp | 2 +- 10 files changed, 210 insertions(+), 41 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 1a38079e..059c5760 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -22,10 +22,12 @@ jobs: with: name: tests.${{ inputs.platform }}-${{ inputs.architecture }} path: ./ - + - name: Setup Dependencies (Linux) if: inputs.platform == 'linux' - run: sudo apt-get update && sudo apt-get install -y libgtk-4-dev + run: | + sudo apt-get update + sudo apt-get install libwayland-dev wayland-protocols libudev-dev libdecor-0-dev - name: Extract Tests Archive (Windows) if: inputs.platform == 'win' diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 8d03b5fc..9228d54e 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -108,6 +108,8 @@ void InitSample(void* payload) // TODO: For now we need to put the heap as GpuUpload but it should be Gpu when we use IOQueues // TODO: Having GPU Upload is still annoying 😞 + // TODO: For the moment we implement all textures of the scene in GPU memory. That is why we need 2GB for bistro scene + // We will implement virtual texturing/texture streaming in the future applicationPayload->GpuMemory = SampleCreateGpuMemory(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(2048)); applicationPayload->FrameDataBuffer = SampleCreateGpuBufferAndUploadData(&applicationPayload->GpuMemory, &applicationPayload->FrameData, sizeof(ShaderFrameData), "FrameData"); diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index ddbc72e1..b29b11bd 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -176,7 +176,7 @@ ElemCommandList MetalGetCommandList(ElemCommandQueue commandQueue, const ElemCom { metalCommandBuffer->setLabel(NS::String::string(options->DebugName, NS::UTF8StringEncoding)); } - + auto resourceBarrierPool = CreateResourceBarrierPool(MetalGraphicsMemoryArena); auto handle = SystemAddDataPoolItem(metalCommandListPool, { diff --git a/src/Elemental/Apple/Graphics/MetalConfig.h b/src/Elemental/Apple/Graphics/MetalConfig.h index ca14cf24..2acf7192 100644 --- a/src/Elemental/Apple/Graphics/MetalConfig.h +++ b/src/Elemental/Apple/Graphics/MetalConfig.h @@ -1,4 +1,4 @@ -#pragma onc +#pragma once #define METAL_MEMORY_ARENA 128 * 1024 * 1024 #define METAL_READBACK_MEMORY_ARENA 32 * 1024 * 1024 @@ -10,6 +10,7 @@ #define METAL_MAX_GRAPHICSHEAP 32 #define METAL_MAX_RESOURCES 1000000 +#define METAL_MAX_SAMPLERS 2048 #define METAL_MAX_SWAPCHAINS 10u diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp index 70267581..82e78a16 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.cpp @@ -8,7 +8,7 @@ struct MetalArgumentBufferDescriptor { uint64_t BufferAddress; - uint64_t TextureResourceId; + uint64_t ResourceId; uint64_t Metadata; }; @@ -139,6 +139,37 @@ ElemGraphicsDeviceInfo MetalConstructGraphicsDeviceInfo(const MTL::Device* devic }; } +MTL::CompareFunction ConvertToMetalCompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return MTL::CompareFunctionNever; + + case ElemGraphicsCompareFunction_Less: + return MTL::CompareFunctionLess; + + case ElemGraphicsCompareFunction_Equal: + return MTL::CompareFunctionEqual; + + case ElemGraphicsCompareFunction_LessEqual: + return MTL::CompareFunctionLessEqual; + + case ElemGraphicsCompareFunction_Greater: + return MTL::CompareFunctionGreater; + + case ElemGraphicsCompareFunction_NotEqual: + return MTL::CompareFunctionNotEqual; + + case ElemGraphicsCompareFunction_GreaterEqual: + return MTL::CompareFunctionGreaterEqual; + + case ElemGraphicsCompareFunction_Always: + return MTL::CompareFunctionAlways; + } +} + + bool MetalCheckGraphicsDeviceCompatibility(const MTL::Device* device) { // TODO: This crash on ios simulator @@ -206,7 +237,36 @@ uint32_t CreateMetalArgumentBufferHandleForTexture(MetalArgumentBuffer argumentB } auto argumentBufferData = (MetalArgumentBufferDescriptor*)storage->ArgumentBuffer->contents(); - argumentBufferData[argumentIndex].TextureResourceId = (uint64_t)texture->gpuResourceID()._impl; + argumentBufferData[argumentIndex].ResourceId = (uint64_t)texture->gpuResourceID()._impl; + + return argumentIndex; +} + +uint32_t CreateMetalArgumentBufferHandleForSamplerState(MetalArgumentBuffer argumentBuffer, MTL::SamplerState* sampler) +{ + SystemAssert(argumentBuffer.Storage); + + auto storage = argumentBuffer.Storage; + auto argumentIndex = UINT32_MAX; + + do + { + if (storage->FreeListIndex == UINT32_MAX) + { + argumentIndex = UINT32_MAX; + break; + } + + argumentIndex = storage->FreeListIndex; + } while (!SystemAtomicCompareExchange(storage->FreeListIndex, argumentIndex, storage->Items[storage->FreeListIndex].Next)); + + if (argumentIndex == UINT32_MAX) + { + argumentIndex = SystemAtomicAdd(storage->CurrentIndex, 1); + } + + auto argumentBufferData = (MetalArgumentBufferDescriptor*)storage->ArgumentBuffer->contents(); + argumentBufferData[argumentIndex].BufferAddress = (uint64_t)sampler->gpuResourceID()._impl; return argumentIndex; } @@ -319,6 +379,7 @@ ElemGraphicsDevice MetalCreateGraphicsDevice(const ElemGraphicsDeviceOptions* op auto memoryArena = SystemAllocateMemoryArena(); auto resourceArgumentBuffer = CreateMetalArgumentBuffer(device, memoryArena, METAL_MAX_RESOURCES); + auto samplerArgumentBuffer = CreateMetalArgumentBuffer(device, memoryArena, METAL_MAX_SAMPLERS); auto residencySetDescriptor = NS::TransferPtr(MTL::ResidencySetDescriptor::alloc()->init()); residencySetDescriptor->setInitialCapacity(8); @@ -328,6 +389,7 @@ ElemGraphicsDevice MetalCreateGraphicsDevice(const ElemGraphicsDeviceOptions* op SystemAssert(error == nullptr); residencySet->addAllocation(resourceArgumentBuffer.Storage->ArgumentBuffer.get()); + residencySet->addAllocation(samplerArgumentBuffer.Storage->ArgumentBuffer.get()); residencySet->commit(); // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) @@ -336,6 +398,7 @@ ElemGraphicsDevice MetalCreateGraphicsDevice(const ElemGraphicsDeviceOptions* op auto handle = SystemAddDataPoolItem(metalGraphicsDevicePool, { .Device = device, .ResourceArgumentBuffer = resourceArgumentBuffer, + .SamplerArgumentBuffer = samplerArgumentBuffer, .MemoryArena = memoryArena, .UploadBufferPools = uploadBuffers }); diff --git a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h index e01809bd..4bcde811 100644 --- a/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h +++ b/src/Elemental/Apple/Graphics/MetalGraphicsDevice.h @@ -15,6 +15,7 @@ struct MetalGraphicsDeviceData { NS::SharedPtr Device; MetalArgumentBuffer ResourceArgumentBuffer; + MetalArgumentBuffer SamplerArgumentBuffer; MemoryArena MemoryArena; uint64_t UploadBufferGeneration; Span>*> UploadBufferPools; @@ -33,8 +34,11 @@ extern bool MetalDebugBarrierInfoEnabled; MetalGraphicsDeviceData* GetMetalGraphicsDeviceData(ElemGraphicsDevice graphicsDevice); MetalGraphicsDeviceDataFull* GetMetalGraphicsDeviceDataFull(ElemGraphicsDevice graphicsDevice); +MTL::CompareFunction ConvertToMetalCompareFunction(ElemGraphicsCompareFunction compareFunction); + uint32_t CreateMetalArgumentBufferHandleForTexture(MetalArgumentBuffer argumentBuffer, MTL::Texture* texture); uint32_t CreateMetalArgumentBufferHandleForBuffer(MetalArgumentBuffer argumentBuffer, MTL::Buffer* buffer, uint32_t length); +uint32_t CreateMetalArgumentBufferHandleForSamplerState(MetalArgumentBuffer argumentBuffer, MTL::SamplerState* sampler); void FreeMetalArgumentBufferHandle(MetalArgumentBuffer argumentBuffer, uint64_t handle); void MetalSetGraphicsOptions(const ElemGraphicsOptions* options); diff --git a/src/Elemental/Apple/Graphics/MetalResource.cpp b/src/Elemental/Apple/Graphics/MetalResource.cpp index 757f28c3..4f8d6a5d 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.cpp +++ b/src/Elemental/Apple/Graphics/MetalResource.cpp @@ -12,6 +12,7 @@ SystemDataPool metalGraphicsHeapPool; SystemDataPool metalResourcePool; Span metalResourceDescriptorInfos; +Span metalSamplerInfos; MemoryArena metalReadBackMemoryArena; thread_local UploadBufferDevicePool> threadDirectX12UploadBufferPools[METAL_MAX_DEVICES]; @@ -25,6 +26,7 @@ void InitMetalResourceMemory() // TODO: This should be part of the graphics device data metalResourceDescriptorInfos = SystemPushArray(MetalGraphicsMemoryArena, METAL_MAX_RESOURCES, AllocationState_Reserved); + metalSamplerInfos = SystemPushArray(MetalGraphicsMemoryArena, METAL_MAX_SAMPLERS, AllocationState_Reserved); // TODO: Allow to increase the size as a parameter metalReadBackMemoryArena = SystemAllocateMemoryArena(METAL_READBACK_MEMORY_ARENA); @@ -612,7 +614,8 @@ void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopy if (resourceData->Type == ElemGraphicsResourceType_Texture2D) { - if (resourceData->Format == ElemGraphicsFormat_BC7_SRGB) + if (resourceData->Format == ElemGraphicsFormat_BC7 || + resourceData->Format == ElemGraphicsFormat_BC7_SRGB) { uploadBufferAlignment = 16u; uploadBufferSizeInBytes = SystemAlign(uploadBufferSizeInBytes, 16u); @@ -663,7 +666,8 @@ void MetalCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopy auto sourceBytesPerRow = mipWidth * 4; - if (resourceData->Format == ElemGraphicsFormat_BC7_SRGB) + if (resourceData->Format == ElemGraphicsFormat_BC7 || + resourceData->Format == ElemGraphicsFormat_BC7_SRGB) { sourceBytesPerRow = ((mipWidth + 3) / 4) * 16; } @@ -786,16 +790,130 @@ void MetalProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) } } +MTL::SamplerMinMagFilter ConvertToMetalMinMagFilter(ElemGraphicsSamplerFilter filter) +{ + switch (filter) + { + case ElemGraphicsSamplerFilter_Nearest: + return MTL::SamplerMinMagFilterNearest; + + case ElemGraphicsSamplerFilter_Linear: + return MTL::SamplerMinMagFilterLinear; + } +} + +MTL::SamplerMipFilter ConvertToMetalMipFilter(ElemGraphicsSamplerFilter filter) +{ + switch (filter) + { + case ElemGraphicsSamplerFilter_Nearest: + return MTL::SamplerMipFilterNearest; + + case ElemGraphicsSamplerFilter_Linear: + return MTL::SamplerMipFilterLinear; + } +} + +MTL::SamplerAddressMode ConvertToMetalSamplerAddressMode(ElemGraphicsSamplerAddressMode addressMode) +{ + switch (addressMode) + { + case ElemGraphicsSamplerAddressMode_Repeat: + return MTL::SamplerAddressModeRepeat; + + case ElemGraphicsSamplerAddressMode_RepeatMirror: + return MTL::SamplerAddressModeMirrorRepeat; + + case ElemGraphicsSamplerAddressMode_ClampToEdge: + return MTL::SamplerAddressModeClampToEdge; + + case ElemGraphicsSamplerAddressMode_ClampToEdgeMirror: + return MTL::SamplerAddressModeMirrorClampToEdge; + + case ElemGraphicsSamplerAddressMode_ClampToBorderColor: + return MTL::SamplerAddressModeClampToBorderColor; + } +} + ElemGraphicsSampler MetalCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) { - return {}; + InitMetalResourceMemory(); + + auto graphicsDeviceData = GetMetalGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto localSamplerInfo = *samplerInfo; + + if (localSamplerInfo.MaxAnisotropy == 0) + { + localSamplerInfo.MaxAnisotropy = 1; + } + + auto borderColor = MTL::SamplerBorderColorOpaqueBlack; + + if (localSamplerInfo.BorderColor.Red == 1.0f && localSamplerInfo.BorderColor.Green == 1.0f && localSamplerInfo.BorderColor.Blue == 1.0f && localSamplerInfo.BorderColor.Alpha == 1.0f) + { + borderColor = MTL::SamplerBorderColorOpaqueWhite; + } + + auto samplerDescriptor = NS::TransferPtr(MTL::SamplerDescriptor::alloc()->init()); + samplerDescriptor->setSupportArgumentBuffers(true); + samplerDescriptor->setMinFilter(ConvertToMetalMinMagFilter(localSamplerInfo.MinFilter)); + samplerDescriptor->setMagFilter(ConvertToMetalMinMagFilter(localSamplerInfo.MagFilter)); + samplerDescriptor->setMipFilter(ConvertToMetalMipFilter(localSamplerInfo.MipFilter)); + samplerDescriptor->setSAddressMode(ConvertToMetalSamplerAddressMode(localSamplerInfo.AddressU)); + samplerDescriptor->setTAddressMode(ConvertToMetalSamplerAddressMode(localSamplerInfo.AddressV)); + samplerDescriptor->setRAddressMode(ConvertToMetalSamplerAddressMode(localSamplerInfo.AddressW)); + samplerDescriptor->setMaxAnisotropy(localSamplerInfo.MaxAnisotropy); + samplerDescriptor->setCompareFunction(ConvertToMetalCompareFunction(localSamplerInfo.CompareFunction)); + samplerDescriptor->setBorderColor(borderColor); + samplerDescriptor->setLodMinClamp(localSamplerInfo.MinLod); + samplerDescriptor->setLodMaxClamp(localSamplerInfo.MaxLod == 0 ? 1000 : localSamplerInfo.MaxLod); + + auto sampler = NS::TransferPtr(graphicsDeviceData->Device->newSamplerState(samplerDescriptor.get())); + + auto handle = CreateMetalArgumentBufferHandleForSamplerState(graphicsDeviceData->SamplerArgumentBuffer, sampler.get()); + + if ((handle % 1024) == 0) + { + SystemCommitMemory(MetalGraphicsMemoryArena, &metalSamplerInfos[handle], 1024 * sizeof(MetalGraphicsSamplerInfo)); + } + + metalSamplerInfos[handle].MetalSampler = sampler; + metalSamplerInfos[handle].SamplerInfo = localSamplerInfo; + + return handle; } ElemGraphicsSamplerInfo MetalGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) { - return {}; + InitMetalResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return {}; + } + + return metalSamplerInfos[sampler].SamplerInfo; } void MetalFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) { + InitMetalResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return; + } + + if (options && options->FencesToWait.Length > 0) + { + EnqueueResourceDeleteEntry(MetalGraphicsMemoryArena, sampler, ResourceDeleteType_Sampler, options->FencesToWait); + return; + } + + metalSamplerInfos[sampler].MetalSampler.reset(); + metalSamplerInfos[sampler] = {}; } diff --git a/src/Elemental/Apple/Graphics/MetalResource.h b/src/Elemental/Apple/Graphics/MetalResource.h index 21b6b336..671d78fe 100644 --- a/src/Elemental/Apple/Graphics/MetalResource.h +++ b/src/Elemental/Apple/Graphics/MetalResource.h @@ -33,6 +33,12 @@ struct MetalResourceDataFull ElemGraphicsDevice GraphicsDevice; }; +struct MetalGraphicsSamplerInfo +{ + NS::SharedPtr MetalSampler; + ElemGraphicsSamplerInfo SamplerInfo; +}; + MetalGraphicsHeapData* GetMetalGraphicsHeapData(ElemGraphicsHeap graphicsHeap); MetalGraphicsHeapDataFull* GetMetalGraphicsHeapDataFull(ElemGraphicsHeap graphicsHeap); diff --git a/src/Elemental/Apple/Graphics/MetalShader.cpp b/src/Elemental/Apple/Graphics/MetalShader.cpp index 34c1a09e..c5eb96c6 100644 --- a/src/Elemental/Apple/Graphics/MetalShader.cpp +++ b/src/Elemental/Apple/Graphics/MetalShader.cpp @@ -188,36 +188,6 @@ MTL::BlendFactor ConvertToMetalBlendFactor(ElemGraphicsBlendFactor blendFactor) } } -MTL::CompareFunction ConvertToMetalCompareFunction(ElemGraphicsCompareFunction compareFunction) -{ - switch (compareFunction) - { - case ElemGraphicsCompareFunction_Never: - return MTL::CompareFunctionNever; - - case ElemGraphicsCompareFunction_Less: - return MTL::CompareFunctionLess; - - case ElemGraphicsCompareFunction_Equal: - return MTL::CompareFunctionEqual; - - case ElemGraphicsCompareFunction_LessEqual: - return MTL::CompareFunctionLessEqual; - - case ElemGraphicsCompareFunction_Greater: - return MTL::CompareFunctionGreater; - - case ElemGraphicsCompareFunction_NotEqual: - return MTL::CompareFunctionNotEqual; - - case ElemGraphicsCompareFunction_GreaterEqual: - return MTL::CompareFunctionGreaterEqual; - - case ElemGraphicsCompareFunction_Always: - return MTL::CompareFunctionAlways; - } -} - ElemShaderLibrary MetalCreateShaderLibrary(ElemGraphicsDevice graphicsDevice, ElemDataSpan shaderLibraryData) { InitMetalShaderLibraryMemory(); @@ -525,6 +495,7 @@ void MetalBindPipelineState(ElemCommandList commandList, ElemPipelineState pipel commandListData->PipelineState = ELEM_HANDLE_NULL; computeCommandEncoder->setBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + computeCommandEncoder->setBuffer(graphicsDeviceData->SamplerArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 1); } if (commandListData->PipelineState != pipelineState) @@ -561,7 +532,9 @@ void MetalPushPipelineStateConstants(ElemCommandList commandList, uint32_t offse if (!commandListData->ArgumentBufferBound) { renderCommandEncoder->setMeshBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + renderCommandEncoder->setMeshBuffer(graphicsDeviceData->SamplerArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 1); renderCommandEncoder->setFragmentBuffer(graphicsDeviceData->ResourceArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 0); + renderCommandEncoder->setFragmentBuffer(graphicsDeviceData->SamplerArgumentBuffer.Storage->ArgumentBuffer.get(), 0, 1); commandListData->ArgumentBufferBound = true; } diff --git a/src/ElementalTools/Shaders/MetalShaderConverter.cpp b/src/ElementalTools/Shaders/MetalShaderConverter.cpp index c03fad8d..e3502e35 100644 --- a/src/ElementalTools/Shaders/MetalShaderConverter.cpp +++ b/src/ElementalTools/Shaders/MetalShaderConverter.cpp @@ -125,7 +125,7 @@ ElemShaderCompilationResult MetalShaderConverterCompileShader(MemoryArena memory { .NumParameters = 1, .pParameters = &rootParameter, - .Flags = IRRootSignatureFlags(IRRootSignatureFlagCBVSRVUAVHeapDirectlyIndexed) + .Flags = IRRootSignatureFlags(IRRootSignatureFlagCBVSRVUAVHeapDirectlyIndexed | IRRootSignatureFlagSamplerHeapDirectlyIndexed) } }; IRError* rootSignatureError = nullptr; From 036e2f4525e4f5351c27cedf291de2330246f100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Wed, 15 Jan 2025 15:33:46 +0100 Subject: [PATCH 89/93] Fix metal command list test --- .../Apple/Graphics/MetalCommandList.cpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index b29b11bd..dec5626b 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -212,6 +212,29 @@ ElemFence MetalExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLis auto commandQueueData = GetMetalCommandQueueData(commandQueue); SystemAssert(commandQueueData); + + if (options && options->FencesToWait.Length > 0) + { + for (uint32_t i = 0; i < options->FencesToWait.Length; i++) + { + auto fenceToWait = options->FencesToWait.Items[i]; + + auto commandQueueToWaitData = GetMetalCommandQueueData(fenceToWait.CommandQueue); + SystemAssert(commandQueueToWaitData); + + auto commandQueueToWaitDataFull = GetMetalCommandQueueDataFull(fenceToWait.CommandQueue); + SystemAssert(commandQueueToWaitDataFull); + + auto commandBuffer = NS::TransferPtr(commandQueueToWaitData->DeviceObject->commandBufferWithUnretainedReferences()); + commandBuffer->encodeWait(commandQueueToWaitData->QueueEvent.get(), fenceToWait.FenceValue); + commandBuffer->commit(); + + if (MetalDebugBarrierInfoEnabled) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Waiting for fence before ExecuteCommandLists. (CommandQueue=%d, Value=%d)", fenceToWait.CommandQueue, fenceToWait.FenceValue); + } + } + } ElemFence fence = {}; From f37a0e3e6ca6874832f09966a2ed6398634a96cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 17 Jan 2025 16:40:41 +0100 Subject: [PATCH 90/93] Update Vulkan --- external/DirectXShaderCompiler/CMakeLists.txt | 2 +- samples/Elemental/02-HelloTriangle/main.c | 1 - .../99-Renderer/Data/RenderMesh.hlsl | 2 +- samples/Elemental/99-Renderer/main.c | 89 +++- .../Apple/Graphics/MetalCommandList.cpp | 4 +- .../Common/Graphics/ResourceBarrier.cpp | 13 +- .../Common/Graphics/ResourceBarrier.h | 1 + .../Graphics/Vulkan/VulkanCommandList.cpp | 18 +- .../Graphics/Vulkan/VulkanCommandList.h | 4 + .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 227 ++++++--- .../Graphics/Vulkan/VulkanGraphicsDevice.h | 13 + .../Common/Graphics/Vulkan/VulkanResource.cpp | 477 ++++++++++++++++-- .../Common/Graphics/Vulkan/VulkanResource.h | 15 +- .../Graphics/Vulkan/VulkanResourceBarrier.cpp | 1 - .../Common/Graphics/Vulkan/VulkanShader.cpp | 39 +- .../Graphics/Vulkan/VulkanSwapChain.cpp | 28 +- src/Elemental/Elemental.h | 7 +- .../Graphics/DirectX12CommandList.cpp | 6 +- .../Microsoft/Graphics/DirectX12Config.h | 2 +- .../Microsoft/Graphics/DirectX12Resource.cpp | 12 +- .../Shaders/DirectXShaderCompiler.cpp | 14 +- tests/GraphicsTests/GraphicsTests.cpp | 45 +- tests/GraphicsTests/GraphicsTests.h | 10 +- tests/GraphicsTests/ResourceIOTests.cpp | 11 +- tests/GraphicsTests/UnityBuild.cpp | 3 + 25 files changed, 846 insertions(+), 198 deletions(-) diff --git a/external/DirectXShaderCompiler/CMakeLists.txt b/external/DirectXShaderCompiler/CMakeLists.txt index 8caa03af..86d01289 100644 --- a/external/DirectXShaderCompiler/CMakeLists.txt +++ b/external/DirectXShaderCompiler/CMakeLists.txt @@ -4,7 +4,7 @@ add_library(dxc INTERFACE) if(WIN32) get_github_release("double-buffer/shader-compilers-bin" ${DXC_VERSION_CUSTOM} "windows_dxc_*_x64.zip" "${CMAKE_CURRENT_BINARY_DIR}/dxc/") - target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/dxc/) + target_include_directories(dxc INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/dxc/include/dxc/) function(copy_dxc_to_target target) file(GLOB DXC_DLLS diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/02-HelloTriangle/main.c index de77d802..b8022d48 100644 --- a/samples/Elemental/02-HelloTriangle/main.c +++ b/samples/Elemental/02-HelloTriangle/main.c @@ -90,7 +90,6 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemCommitCommandList(commandList); ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); - ElemPresentSwapChain(applicationPayload->SwapChain); // NOTE: This is the update part of the loop. If render fast this will create a gap and add latency because we need to wait for the next available diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl index 230dd150..92ba082f 100644 --- a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl +++ b/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl @@ -147,7 +147,7 @@ float4 PixelMain(const VertexOutput input) : SV_Target0 // TODO: Should we use non uniform index here? We know we have the same material for each meshlet. // But we sometimes may group some meshlets together Texture2D albedoTexture = ResourceDescriptorHeap[NonUniformResourceIndex(material.AlbedoTextureId)]; - albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates); + albedo = albedoTexture.Sample(textureSampler, input.TextureCoordinates) * material.AlbedoFactor; // TODO: Doing discard on the main pass is really bad for performance. // Doing it disable the early depth test in the shader, so all pixel shader code has to run for diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Elemental/99-Renderer/main.c index 9228d54e..4129d818 100644 --- a/samples/Elemental/99-Renderer/main.c +++ b/samples/Elemental/99-Renderer/main.c @@ -174,13 +174,13 @@ void FreeSample(void* payload) ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); SampleFreeScene(&applicationPayload->TestSceneData); - SampleFreeGpuBuffer(&applicationPayload->FrameDataBuffer); ElemFreePipelineState(applicationPayload->GraphicsPipeline); ElemFreeSwapChain(applicationPayload->SwapChain); ElemFreeCommandQueue(applicationPayload->CommandQueue); + ElemFreeGraphicsSampler(applicationPayload->ShaderParameters.TextureSampler, NULL); ElemFreeGraphicsResource(applicationPayload->DepthBuffer, NULL); ElemFreeGraphicsHeap(applicationPayload->DepthBufferHeap); @@ -230,56 +230,88 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void // TODO: We need to have a kind of queue system. The problem here is that if we don't have any // data to load we will create empty lists - ElemCommandList loadDataCommandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); + + ElemFence loadDataFence = {}; // TODO: Fow now, we load all the material textures in one shot // We do it in the main loop now because later it will be a streaming system. // Doing it in the main loop force us to decouple the loading - uint32_t testCounter = 0; + uint32_t loadResourceCounter = 0; + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) { SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; if (material->AlbedoTexture && !material->AlbedoTexture->IsLoaded) { - SampleLoadTextureData(loadDataCommandList, material->AlbedoTexture, &applicationPayload->GpuMemory); - testCounter++; + loadResourceCounter++; } if (material->NormalTexture && !material->NormalTexture->IsLoaded) { - SampleLoadTextureData(loadDataCommandList, material->NormalTexture, &applicationPayload->GpuMemory); - testCounter++; + loadResourceCounter++; } } - // TODO: Measure scene loading time - if (testCounter > 0) + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MeshCount; i++) { - printf("TextureCount: %d\n", testCounter); + SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[i]; + + if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) + { + loadResourceCounter++; + } } - for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) + if (loadResourceCounter > 0) { - SampleSceneNodeHeader* sceneNode = &applicationPayload->TestSceneData.Nodes[i]; + // TODO: We should have a queue system instead and load only what is needed otherwise + // each frames we will have empty lists + // This system will allow to split by batches the loading + ElemCommandList loadDataCommandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); - if (sceneNode->NodeType == SampleSceneNodeType_Mesh) + for (uint32_t i = 0; i < applicationPayload->TestSceneData.MaterialCount; i++) { - SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[sceneNode->ReferenceIndex]; + SampleMaterialData* material = &applicationPayload->TestSceneData.Materials[i]; + + if (material->AlbedoTexture && !material->AlbedoTexture->IsLoaded) + { + SampleLoadTextureData(loadDataCommandList, material->AlbedoTexture, &applicationPayload->GpuMemory); + loadResourceCounter++; + } + + if (material->NormalTexture && !material->NormalTexture->IsLoaded) + { + SampleLoadTextureData(loadDataCommandList, material->NormalTexture, &applicationPayload->GpuMemory); + loadResourceCounter++; + } + } - if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) - { - // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data - // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check - // if the data was loaded. - // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. - SampleLoadMeshData(loadDataCommandList, meshData, &applicationPayload->GpuMemory); + for (uint32_t i = 0; i < applicationPayload->TestSceneData.NodeCount; i++) + { + SampleSceneNodeHeader* sceneNode = &applicationPayload->TestSceneData.Nodes[i]; + + if (sceneNode->NodeType == SampleSceneNodeType_Mesh) + { + SampleMeshData* meshData = &applicationPayload->TestSceneData.Meshes[sceneNode->ReferenceIndex]; + + if (meshData->MeshBuffer.Buffer == ELEM_HANDLE_NULL) + { + // TODO: This is an attempt to dissociate the loading of the mesh metadata and the buffer data + // Later, this will be done via streaming in parrallel. The gpu shader that will handle the mesh will need to check + // if the data was loaded. + // For now, we block to load the mesh if not already done which is equavalent at loading the full scene during the init. + SampleLoadMeshData(loadDataCommandList, meshData, &applicationPayload->GpuMemory); + loadResourceCounter++; + } } } - } - ElemCommitCommandList(loadDataCommandList); - ElemFence loadDataFence = ElemExecuteCommandList(applicationPayload->CommandQueue, loadDataCommandList, NULL); + ElemCommitCommandList(loadDataCommandList); // TODO: Measure scene loading time + + printf("ResourceLoadCount: %d\n", loadResourceCounter); + loadDataFence = ElemExecuteCommandList(applicationPayload->CommandQueue, loadDataCommandList, NULL); + } ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); @@ -339,7 +371,14 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemCommitCommandList(commandList); - applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, &(ElemExecuteCommandListOptions) { .FencesToWait = { .Items = &loadDataFence, .Length = 1 }}); + ElemExecuteCommandListOptions executeOptions = {}; + + if (loadResourceCounter > 0) + { + executeOptions.FencesToWait = (ElemFenceSpan){ .Items = &loadDataFence, .Length = 1 }; + } + + applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, &executeOptions); ElemPresentSwapChain(applicationPayload->SwapChain); SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); diff --git a/src/Elemental/Apple/Graphics/MetalCommandList.cpp b/src/Elemental/Apple/Graphics/MetalCommandList.cpp index dec5626b..7bd44710 100644 --- a/src/Elemental/Apple/Graphics/MetalCommandList.cpp +++ b/src/Elemental/Apple/Graphics/MetalCommandList.cpp @@ -202,6 +202,9 @@ void MetalCommitCommandList(ElemCommandList commandList) auto commandListData = GetMetalCommandListData(commandList); SystemAssert(commandListData); + + FreeResourceBarrierPool(commandListData->ResourceBarrierPool); + commandListData->IsCommitted = true; metalThreadCommandBufferCommitted = true; } @@ -265,7 +268,6 @@ ElemFence MetalExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLis commandListData->DeviceObject->commit(); commandListData->DeviceObject.reset(); - FreeResourceBarrierPool(commandListData->ResourceBarrierPool); SystemRemoveDataPoolItem(metalCommandListPool, commandLists.Items[i]); } diff --git a/src/Elemental/Common/Graphics/ResourceBarrier.cpp b/src/Elemental/Common/Graphics/ResourceBarrier.cpp index 80105a75..28cfa1bd 100644 --- a/src/Elemental/Common/Graphics/ResourceBarrier.cpp +++ b/src/Elemental/Common/Graphics/ResourceBarrier.cpp @@ -5,7 +5,9 @@ #include "SystemLogging.h" #define GRAPHICS_MAX_RESOURCEBARRIERPOOL 64 -#define GRAPHICS_MAX_RESOURCEBARRIER_RESOURCES 128 + +// TODO: Check that it takes a lot of memory +#define GRAPHICS_MAX_RESOURCEBARRIER_RESOURCES 64 struct ResourceBarrierResourceStatus { @@ -15,6 +17,8 @@ struct ResourceBarrierResourceStatus ElemGraphicsResourceBarrierLayoutType LastLayoutType; }; +// TODO: To save huge amount of memory allocation, we should allocate thoses in a separate arena +// and use reserved memory struct ResourceBarrierPoolData { uint32_t BarrierCount; @@ -150,7 +154,7 @@ void EnqueueBarrier(ResourceBarrierPool barrierPool, ElemGraphicsResourceDescrip void EnqueueBarrier(ResourceBarrierPool barrierPool, const ResourceBarrierItem* resourceBarrier) { SystemAssert(barrierPool != ELEM_HANDLE_NULL); - + auto barrierPoolData = SystemGetDataPoolItem(resourceBarrierDataPool, barrierPool); SystemAssert(barrierPoolData); @@ -231,6 +235,11 @@ ResourceBarriers GenerateBarrierCommands(MemoryArena memoryArena, ResourceBarrie if (!resourceStatus) { + if (barrierPoolData->ResourceStatusCount + 1 >= GRAPHICS_MAX_RESOURCEBARRIER_RESOURCES) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Command list barrier pool resources count exceeded."); + } + resourceStatus = &barrierPoolData->ResourceStatus[barrierPoolData->ResourceStatusCount++]; *resourceStatus = diff --git a/src/Elemental/Common/Graphics/ResourceBarrier.h b/src/Elemental/Common/Graphics/ResourceBarrier.h index 780b256a..8b806c09 100644 --- a/src/Elemental/Common/Graphics/ResourceBarrier.h +++ b/src/Elemental/Common/Graphics/ResourceBarrier.h @@ -18,6 +18,7 @@ struct ResourceBarrierItem ElemGraphicsResourceBarrierAccessType AfterAccess; ElemGraphicsResourceBarrierLayoutType BeforeLayout; ElemGraphicsResourceBarrierLayoutType AfterLayout; + // TODO: Add offset and size for buffer }; struct ResourceBarriers diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index 57563e3a..4d55b730 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -237,6 +237,7 @@ ElemCommandList VulkanGetCommandList(ElemCommandQueue commandQueue, const ElemCo VkCommandPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; createInfo.queueFamilyIndex = commandQueueData->QueueFamilyIndex; + createInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; VkCommandPool commandPool; AssertIfFailed(vkCreateCommandPool(graphicsDeviceData->Device, &createInfo, 0, &commandPool)); @@ -290,7 +291,8 @@ ElemCommandList VulkanGetCommandList(ElemCommandQueue commandQueue, const ElemCo .CommandQueue = commandQueue, .CommandAllocatorPoolItem = commandAllocatorPoolItem, .CommandListPoolItem = commandListPoolItem, - .ResourceBarrierPool = resourceBarrierPool + .ResourceBarrierPool = resourceBarrierPool, + .UploadBufferCount = 0 }); SystemAddDataPoolItemFull(vulkanCommandListPool, handle, { @@ -307,6 +309,8 @@ void VulkanCommitCommandList(ElemCommandList commandList) auto commandListData = GetVulkanCommandListData(commandList); SystemAssert(commandListData); + + FreeResourceBarrierPool(commandListData->ResourceBarrierPool); AssertIfFailed(vkEndCommandBuffer(commandListData->DeviceObject)); @@ -340,7 +344,7 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi auto commandQueueToWaitData = GetVulkanCommandQueueData(fenceToWait.CommandQueue); SystemAssert(commandQueueToWaitData); - submitStageMasks[i] = VK_PIPELINE_STAGE_TASK_SHADER_BIT_EXT | VK_PIPELINE_STAGE_MESH_SHADER_BIT_EXT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + submitStageMasks[i] = VK_PIPELINE_STAGE_MESH_SHADER_BIT_EXT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; waitSemaphores[i] = commandQueueToWaitData->Fence; waitSemaphoreValues[i] = fenceToWait.FenceValue; @@ -417,8 +421,13 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi } UpdateCommandAllocatorPoolItemFence(commandListData->CommandAllocatorPoolItem, fence); + + for (uint32_t j = 0; j < commandListData->UploadBufferCount; j++) + { + UpdateUploadBufferPoolItemFence(commandListData->UploadBufferPoolItems[j], fence); + } + ReleaseCommandListPoolItem(commandListData->CommandListPoolItem); - FreeResourceBarrierPool(commandListData->ResourceBarrierPool); SystemRemoveDataPoolItem(vulkanCommandListPool, commandLists.Items[i]); } @@ -446,7 +455,8 @@ void VulkanWaitForFenceOnCpu(ElemFence fence) if (fence.FenceValue > commandQueueToWaitData->LastCompletedFenceValue) { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for fence on CPU..."); + // TODO: Activate it in a special debug mode + //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for fence on CPU..."); VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO }; waitInfo.semaphoreCount = 1; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h index 0218efe0..db5f7a7d 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h @@ -1,8 +1,10 @@ #pragma once #include "Elemental.h" +#include "VulkanResource.h" #include "Graphics/CommandAllocatorPool.h" #include "Graphics/ResourceBarrier.h" +#include "Graphics/UploadBufferPool.h" #include "SystemSpan.h" #include "volk.h" @@ -44,6 +46,8 @@ struct VulkanCommandListData CommandListPoolItem* CommandListPoolItem; VulkanPipelineStateType PipelineStateType; ResourceBarrierPool ResourceBarrierPool; + UploadBufferPoolItem* UploadBufferPoolItems[MAX_UPLOAD_BUFFERS]; + uint32_t UploadBufferCount; }; struct VulkanCommandListDataFull diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 598b2e13..2835c617 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -4,6 +4,12 @@ #include "SystemLogging.h" #include "SystemMemory.h" +struct VulkanDescriptorSet +{ + VkDescriptorPool DescriptorPool; + VkDescriptorSet DescriptorSet; +}; + struct VulkanDescriptorHeapFreeListItem { uint32_t Next; @@ -11,8 +17,7 @@ struct VulkanDescriptorHeapFreeListItem struct VulkanDescriptorHeapStorage { - VkDescriptorPool DescriptorPool; - VkDescriptorSet DescriptorSet; + const VulkanDescriptorSet* DescriptorSet; Span Items; uint32_t CurrentIndex; uint32_t FreeListIndex; @@ -45,6 +50,11 @@ VkBool32 VKAPI_CALL VulkanDebugReportCallback(VkDebugReportFlagsEXT flags, VkDeb return VK_FALSE; } + if (SystemFindSubString(pMessage, "BestPractices-PushConstants") != -1) + { + return VK_FALSE; + } + SystemLogMessage(messageType, ElemLogMessageCategory_Graphics, "%s", pMessage); return VK_FALSE; @@ -186,6 +196,36 @@ void InitVulkanGraphicsDeviceMemory() } } +VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return VK_COMPARE_OP_NEVER; + + case ElemGraphicsCompareFunction_Less: + return VK_COMPARE_OP_LESS; + + case ElemGraphicsCompareFunction_Equal: + return VK_COMPARE_OP_EQUAL; + + case ElemGraphicsCompareFunction_LessEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + + case ElemGraphicsCompareFunction_Greater: + return VK_COMPARE_OP_GREATER; + + case ElemGraphicsCompareFunction_NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + + case ElemGraphicsCompareFunction_GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + + case ElemGraphicsCompareFunction_Always: + return VK_COMPARE_OP_ALWAYS; + } +} + ElemGraphicsDeviceInfo VulkanConstructGraphicsDeviceInfo(MemoryArena memoryArena, VkPhysicalDeviceProperties deviceProperties, VkPhysicalDeviceMemoryProperties deviceMemoryProperties) { auto deviceName = ReadOnlySpan(deviceProperties.deviceName); @@ -201,26 +241,26 @@ ElemGraphicsDeviceInfo VulkanConstructGraphicsDeviceInfo(MemoryArena memoryArena }; } -VulkanDescriptorHeap CreateVulkanDescriptorHeap(VkDevice graphicsDevice, MemoryArena memoryArena, VkDescriptorSetLayout descriptorSetLayout, uint32_t length) +VulkanDescriptorSet* CreateVulkanDescriptorSet(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorCount) { - // TODO: Use VK_DESCRIPTOR BUFFER? - VkDescriptorPoolSize poolSizes[] + VkDescriptorPoolSize poolSize { - {VK_DESCRIPTOR_TYPE_MUTABLE_EXT, length } + .type = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, + .descriptorCount = descriptorCount }; VkDescriptorPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - createInfo.poolSizeCount = ARRAYSIZE(poolSizes); - createInfo.pPoolSizes = poolSizes; + createInfo.poolSizeCount = 1; + createInfo.pPoolSizes = &poolSize; createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; - createInfo.maxSets = ARRAYSIZE(poolSizes); + createInfo.maxSets = 1; VkDescriptorPool descriptorPool; AssertIfFailed(vkCreateDescriptorPool(graphicsDevice, &createInfo, nullptr, &descriptorPool)); VkDescriptorSetVariableDescriptorCountAllocateInfo descriptorSetLayoutCount = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO }; descriptorSetLayoutCount.descriptorSetCount = 1; - descriptorSetLayoutCount.pDescriptorCounts = &length; + descriptorSetLayoutCount.pDescriptorCounts = &descriptorCount; VkDescriptorSetAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; allocateInfo.pSetLayouts = &descriptorSetLayout; @@ -230,9 +270,25 @@ VulkanDescriptorHeap CreateVulkanDescriptorHeap(VkDevice graphicsDevice, MemoryA VkDescriptorSet descriptorSet; AssertIfFailed(vkAllocateDescriptorSets(graphicsDevice, &allocateInfo, &descriptorSet)); + + auto result = SystemPushStruct(memoryArena); + result->DescriptorPool = descriptorPool; + result->DescriptorSet = descriptorSet; + + return result; +} + +void FreeVulkanDescriptorSet(VkDevice device, const VulkanDescriptorSet* descriptorSet) +{ + SystemAssert(descriptorSet); + vkDestroyDescriptorPool(device, descriptorSet->DescriptorPool, nullptr); +} + +VulkanDescriptorHeap CreateVulkanDescriptorHeap(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t length) +{ + auto descriptorSet = CreateVulkanDescriptorSet(memoryArena, graphicsDevice, descriptorSetLayout, VULKAN_MAX_RESOURCES); auto descriptorStorage = SystemPushStruct(memoryArena); - descriptorStorage->DescriptorPool = descriptorPool; descriptorStorage->DescriptorSet = descriptorSet; descriptorStorage->Items = SystemPushArray(memoryArena, length); descriptorStorage->CurrentIndex = 0; @@ -244,10 +300,10 @@ VulkanDescriptorHeap CreateVulkanDescriptorHeap(VkDevice graphicsDevice, MemoryA }; } -void FreeVulkanDescriptorHeap(VkDevice device, VulkanDescriptorHeap descriptorHeap) +void FreeVulkanDescriptorHeap(VkDevice device, const VulkanDescriptorHeap descriptorHeap) { SystemAssert(descriptorHeap.Storage); - vkDestroyDescriptorPool(device, descriptorHeap.Storage->DescriptorPool, nullptr); + vkDestroyDescriptorPool(device, descriptorHeap.Storage->DescriptorSet->DescriptorPool, nullptr); } uint32_t CreateVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap) @@ -319,52 +375,38 @@ VulkanGraphicsDeviceDataFull* GetVulkanGraphicsDeviceDataFull(ElemGraphicsDevice return SystemGetDataPoolItemFull(vulkanGraphicsDevicePool, graphicsDevice); } -void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) +VkDescriptorSetLayout CreateVulkanDescriptorSetLayout(ElemGraphicsDevice graphicsDevice, VkDescriptorType* descriptorTypes, uint32_t descriptorTypeCount) { auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); SystemAssert(graphicsDeviceData); - auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); - SystemAssert(graphicsDeviceDataFull); - - VkPipelineLayoutCreateInfo layoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - layoutCreateInfo.pSetLayouts = nullptr; - layoutCreateInfo.setLayoutCount = 0; - - VkPushConstantRange push_constant; - push_constant.offset = 0; - push_constant.size = 24 * 4; - push_constant.stageFlags = VK_SHADER_STAGE_ALL; - - layoutCreateInfo.pPushConstantRanges = &push_constant; - layoutCreateInfo.pushConstantRangeCount = 1; - - VkDescriptorType resourceDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE }; - - VkMutableDescriptorTypeListEXT descriptorSetTypeList = - { - .descriptorTypeCount = ARRAYSIZE(resourceDescriptorTypes), - .pDescriptorTypes = resourceDescriptorTypes + VkMutableDescriptorTypeListEXT descriptorSetTypes = + { + .descriptorTypeCount = descriptorTypeCount, + .pDescriptorTypes = descriptorTypes }; VkMutableDescriptorTypeCreateInfoEXT mutableDescriptorTypeCreateInfo = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT }; - mutableDescriptorTypeCreateInfo.pMutableDescriptorTypeLists = &descriptorSetTypeList; + mutableDescriptorTypeCreateInfo.pMutableDescriptorTypeLists = &descriptorSetTypes; mutableDescriptorTypeCreateInfo.mutableDescriptorTypeListCount = 1; - VkDescriptorBindingFlags descriptorBindingFlags = VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | - VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT; + VkDescriptorBindingFlags descriptorBindingFlags[] = + { + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT + }; - VkDescriptorSetLayoutBindingFlagsCreateInfo descriptorBindingFlagsCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; + VkDescriptorSetLayoutBindingFlagsCreateInfo descriptorBindingFlagsCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; descriptorBindingFlagsCreateInfo.bindingCount = 1; - descriptorBindingFlagsCreateInfo.pBindingFlags = &descriptorBindingFlags; + descriptorBindingFlagsCreateInfo.pBindingFlags = descriptorBindingFlags; descriptorBindingFlagsCreateInfo.pNext = &mutableDescriptorTypeCreateInfo; - VkDescriptorSetLayoutBinding descriptorBinding = {}; - descriptorBinding.binding = 0; - descriptorBinding.descriptorType = VK_DESCRIPTOR_TYPE_MUTABLE_EXT; - descriptorBinding.descriptorCount = VULKAN_MAX_RESOURCES; - descriptorBinding.stageFlags = VK_SHADER_STAGE_ALL; + VkDescriptorSetLayoutBinding descriptorBinding = + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, + .descriptorCount = VULKAN_MAX_RESOURCES, + .stageFlags = VK_SHADER_STAGE_ALL + }; VkDescriptorSetLayoutCreateInfo descriptorSetCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; descriptorSetCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; @@ -372,10 +414,42 @@ void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) descriptorSetCreateInfo.pBindings = &descriptorBinding; descriptorSetCreateInfo.pNext = &descriptorBindingFlagsCreateInfo; - AssertIfFailed(vkCreateDescriptorSetLayout(graphicsDeviceData->Device, &descriptorSetCreateInfo, 0, &graphicsDeviceDataFull->ResourceDescriptorSetLayout)); + VkDescriptorSetLayout result; + AssertIfFailed(vkCreateDescriptorSetLayout(graphicsDeviceData->Device, &descriptorSetCreateInfo, 0, &result)); + + return result; +} + +void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); + SystemAssert(graphicsDeviceDataFull); + + VkPipelineLayoutCreateInfo layoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + + VkPushConstantRange push_constant; + push_constant.offset = 0; + push_constant.size = 24 * 4; + push_constant.stageFlags = VK_SHADER_STAGE_ALL; + + layoutCreateInfo.pPushConstantRanges = &push_constant; + layoutCreateInfo.pushConstantRangeCount = 1; + + // TODO: Recheck those + VkDescriptorType resourceDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE }; + graphicsDeviceDataFull->ResourceDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, resourceDescriptorTypes, ARRAYSIZE(resourceDescriptorTypes)); + + VkDescriptorType samplerDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_SAMPLER }; + graphicsDeviceDataFull->SamplerDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, samplerDescriptorTypes, ARRAYSIZE(samplerDescriptorTypes)); - layoutCreateInfo.pSetLayouts = &graphicsDeviceDataFull->ResourceDescriptorSetLayout; - layoutCreateInfo.setLayoutCount = 1; + VkDescriptorSetLayout descriptorSetLayouts[] { graphicsDeviceDataFull->ResourceDescriptorSetLayout, graphicsDeviceDataFull->SamplerDescriptorSetLayout }; + layoutCreateInfo.pSetLayouts = descriptorSetLayouts; + layoutCreateInfo.setLayoutCount = ARRAYSIZE(descriptorSetLayouts); AssertIfFailed(vkCreatePipelineLayout(graphicsDeviceData->Device, &layoutCreateInfo, 0, &graphicsDeviceData->PipelineLayout)); } @@ -394,10 +468,7 @@ void VulkanSetGraphicsOptions(const ElemGraphicsOptions* options) vulkanDebugGpuValidationEnabled = options->EnableGpuValidation; } - if (options->EnableDebugBarrierInfo) - { - VulkanDebugBarrierInfoEnabled = options->EnableDebugBarrierInfo; - } + VulkanDebugBarrierInfoEnabled = options->EnableDebugBarrierInfo; } ElemGraphicsDeviceInfoSpan VulkanGetAvailableGraphicsDevices() @@ -518,6 +589,7 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o int32_t gpuMemoryTypeIndex = -1; int32_t gpuUploadMemoryTypeIndex = -1; int32_t readBackMemoryTypeIndex = -1; + int32_t uploadMemoryTypeIndex = -1; for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) { @@ -540,11 +612,11 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o readBackMemoryTypeIndex = i; } - // TODO: Later we will need that to implement IOGraphicsQueue - /* - else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) + else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) { - }*/ + uploadMemoryTypeIndex = i; + } } SystemAssert(gpuMemoryTypeIndex != -1 && gpuUploadMemoryTypeIndex != -1 && readBackMemoryTypeIndex != -1); @@ -571,6 +643,7 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o features.features.shaderInt64 = true; features.features.pipelineStatisticsQuery = true; features.features.fillModeNonSolid = true; + features.features.samplerAnisotropy = true; VkPhysicalDeviceVulkan12Features features12 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; features12.timelineSemaphore = true; @@ -595,13 +668,13 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o features13.maintenance4 = true; features13.synchronization2 = true; features13.dynamicRendering = true; + features13.shaderDemoteToHelperInvocation = true; - VkPhysicalDeviceMaintenance5FeaturesKHR maintenance5 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES_KHR }; + VkPhysicalDeviceMaintenance5Features maintenance5 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES }; maintenance5.maintenance5 = true; VkPhysicalDeviceMeshShaderFeaturesEXT meshFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; meshFeatures.meshShader = true; - meshFeatures.taskShader = true; meshFeatures.meshShaderQueries = true; VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableDescriptorFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT }; @@ -642,7 +715,8 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o .CopyCommandQueueIndex = copyCommandQueueIndex, .GpuMemoryTypeIndex = (uint32_t)gpuMemoryTypeIndex, .GpuUploadMemoryTypeIndex = (uint32_t)gpuUploadMemoryTypeIndex, - .ReadBackMemoryTypeIndex = (uint32_t)readBackMemoryTypeIndex + .ReadBackMemoryTypeIndex = (uint32_t)readBackMemoryTypeIndex, + .UploadMemoryTypeIndex = (uint32_t)uploadMemoryTypeIndex }); CreateVulkanPipelineLayout(handle); @@ -650,8 +724,11 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o auto graphicsDeviceData = GetVulkanGraphicsDeviceData(handle); auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(handle); - auto resourceDescriptorHeap = CreateVulkanDescriptorHeap(graphicsDeviceData->Device, memoryArena, graphicsDeviceDataFull->ResourceDescriptorSetLayout, VULKAN_MAX_RESOURCES); - graphicsDeviceData->ResourceDescriptorHeap = resourceDescriptorHeap; + graphicsDeviceData->ResourceDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, VULKAN_MAX_RESOURCES); + graphicsDeviceData->SamplerDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, VULKAN_MAX_SAMPLER); + + // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) + graphicsDeviceData->UploadBufferPools = SystemPushArray*>(VulkanGraphicsMemoryArena, MAX_UPLOAD_BUFFERS); return handle; } @@ -665,10 +742,36 @@ void VulkanFreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); SystemAssert(graphicsDeviceDataFull); + + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + for (uint32_t j = 0; j < MAX_UPLOAD_BUFFERS; j++) + { + auto uploadBuffer = &bufferPool->UploadBuffers[j]; + + if (uploadBuffer->Buffer.Buffer) + { + vkDestroyBuffer(graphicsDeviceData->Device, uploadBuffer->Buffer.Buffer, nullptr); + vkFreeMemory(graphicsDeviceData->Device, uploadBuffer->Buffer.DeviceMemory, nullptr); + + uploadBuffer->Buffer = {}; + *uploadBuffer = {}; + } + } + + *bufferPool = {}; + } + } FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->ResourceDescriptorHeap); + FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->SamplerDescriptorHeap); vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, nullptr); vkDestroyPipelineLayout(graphicsDeviceData->Device, graphicsDeviceData->PipelineLayout, nullptr); vkDestroyDevice(graphicsDeviceData->Device, nullptr); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h index 6efa9d51..fe358ea2 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h @@ -2,6 +2,8 @@ #include "Elemental.h" #include "SystemMemory.h" +#include "VulkanResource.h" +#include "Graphics/UploadBufferPool.h" #ifdef WIN32 #define VK_USE_PLATFORM_WIN32_KHR @@ -15,9 +17,12 @@ // TODO: Investigate why we cannot bump this to 1 million //#define VULKAN_MAX_RESOURCES 400000 #define VULKAN_MAX_RESOURCES 1000000 +#define VULKAN_MAX_SAMPLER 2048 struct VulkanDescriptorHeapStorage; +struct VulkanDescriptorSet; + struct VulkanDescriptorHeap { VulkanDescriptorHeapStorage* Storage; @@ -29,7 +34,11 @@ struct VulkanGraphicsDeviceData MemoryArena MemoryArena; VkPipelineLayout PipelineLayout; uint64_t CommandAllocationGeneration; + uint64_t UploadBufferGeneration; VulkanDescriptorHeap ResourceDescriptorHeap; + VulkanDescriptorHeap SamplerDescriptorHeap; + Span*> UploadBufferPools; + uint32_t CurrentUploadBufferPoolIndex; }; struct VulkanGraphicsDeviceDataFull @@ -46,7 +55,9 @@ struct VulkanGraphicsDeviceDataFull uint32_t GpuMemoryTypeIndex; uint32_t GpuUploadMemoryTypeIndex; uint32_t ReadBackMemoryTypeIndex; + uint32_t UploadMemoryTypeIndex; VkDescriptorSetLayout ResourceDescriptorSetLayout; + VkDescriptorSetLayout SamplerDescriptorSetLayout; }; extern MemoryArena VulkanGraphicsMemoryArena; @@ -57,6 +68,8 @@ extern bool VulkanDebugBarrierInfoEnabled; VulkanGraphicsDeviceData* GetVulkanGraphicsDeviceData(ElemGraphicsDevice graphicsDevice); VulkanGraphicsDeviceDataFull* GetVulkanGraphicsDeviceDataFull(ElemGraphicsDevice graphicsDevice); +VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction); + void VulkanSetGraphicsOptions(const ElemGraphicsOptions* options); uint32_t CreateVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index a7f01aed..aeaeab30 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -1,18 +1,26 @@ #include "VulkanResource.h" #include "VulkanGraphicsDevice.h" +#include "VulkanCommandList.h" +#include "VulkanResourceBarrier.h" #include "Graphics/Resource.h" +#include "Graphics/ResourceBarrier.h" #include "Graphics/ResourceDeleteQueue.h" +#include "Graphics/UploadBufferPool.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" #define VULKAN_MAX_GRAPHICSHEAP 32 +#define VULKAN_MAX_SAMPLERS 2048 SystemDataPool vulkanGraphicsHeapPool; SystemDataPool vulkanGraphicsResourcePool; +thread_local UploadBufferDevicePool threadVulkanUploadBufferPools[VULKAN_MAX_DEVICES]; + // TODO: This descriptor infos should be linked to the graphics device like the resource desc heaps Span vulkanResourceDescriptorInfos; +Span vulkanSamplerInfos; // TODO: To refactor Span vulkanResourceDescriptorImageViews; MemoryArena vulkanReadBackMemoryArena; @@ -26,6 +34,7 @@ void InitVulkanResourceMemory() vulkanResourceDescriptorInfos = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES, AllocationState_Reserved); vulkanResourceDescriptorImageViews = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_RESOURCES, AllocationState_Reserved); + vulkanSamplerInfos = SystemPushArray(VulkanGraphicsMemoryArena, VULKAN_MAX_SAMPLERS, AllocationState_Reserved); vulkanReadBackMemoryArena = SystemAllocateMemoryArena(32 * 1024 * 1024); } @@ -98,6 +107,51 @@ VkImageUsageFlags ConvertToVulkanImageUsageFlags(ElemGraphicsResourceUsage usage return result; } +VkFilter ConvertToVulkanFilter(ElemGraphicsSamplerFilter filter) +{ + switch (filter) + { + case ElemGraphicsSamplerFilter_Nearest: + return VK_FILTER_NEAREST; + + case ElemGraphicsSamplerFilter_Linear: + return VK_FILTER_LINEAR; + } +} + +VkSamplerMipmapMode ConvertToVulkanSamplerMipMapMode(ElemGraphicsSamplerFilter filter) +{ + switch (filter) + { + case ElemGraphicsSamplerFilter_Nearest: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + + case ElemGraphicsSamplerFilter_Linear: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + } +} + +VkSamplerAddressMode ConvertToVulkanSamplerAddressMode(ElemGraphicsSamplerAddressMode addressMode) +{ + switch (addressMode) + { + case ElemGraphicsSamplerAddressMode_Repeat: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + + case ElemGraphicsSamplerAddressMode_RepeatMirror: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + + case ElemGraphicsSamplerAddressMode_ClampToEdge: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + + case ElemGraphicsSamplerAddressMode_ClampToEdgeMirror: + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + + case ElemGraphicsSamplerAddressMode_ClampToBorderColor: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + } +} + ElemGraphicsResource CreateVulkanTextureFromResource(ElemGraphicsDevice graphicsDevice, VkImage resource, const ElemGraphicsResourceInfo* resourceInfo, bool isPresentTexture) { InitVulkanResourceMemory(); @@ -243,8 +297,12 @@ ElemGraphicsHeap VulkanCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uin allocateInfo.allocationSize = sizeInBytes; allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuMemoryTypeIndex; + auto heapType = ElemGraphicsHeapType_Gpu; + if (options) { + heapType = options->HeapType; + if (options->HeapType == ElemGraphicsHeapType_GpuUpload) { allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->GpuUploadMemoryTypeIndex; @@ -270,6 +328,7 @@ ElemGraphicsHeap VulkanCreateGraphicsHeap(ElemGraphicsDevice graphicsDevice, uin auto handle = SystemAddDataPoolItem(vulkanGraphicsHeapPool, { .DeviceObject = deviceMemory, + .HeapType = heapType, .SizeInBytes = sizeInBytes, .GraphicsDevice = graphicsDevice, }); @@ -527,24 +586,28 @@ void VulkanUploadGraphicsBufferData(ElemGraphicsResource resource, uint32_t offs SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers."); return; } + + auto resourceDataFull = GetVulkanGraphicsResourceDataFull(resource); + SystemAssert(resourceDataFull); + + auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); + SystemAssert(graphicsHeapData); + + if (graphicsHeapData->HeapType != ElemGraphicsHeapType_GpuUpload) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemUploadGraphicsBufferData only works with graphics buffers allocated in a GpuUpload heap."); + return; + } - if (resourceData->CpuDataPointer == nullptr) - { - auto resourceDataFull = GetVulkanGraphicsResourceDataFull(resource); - SystemAssert(resourceDataFull); + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(resourceDataFull->GraphicsDevice); + SystemAssert(graphicsDeviceData); - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(resourceDataFull->GraphicsDevice); - SystemAssert(graphicsDeviceData); + uint8_t* cpuPointer = nullptr; + AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset + offset, data.Length, 0, (void**)&cpuPointer)); - auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); - SystemAssert(graphicsHeapData); + memcpy(cpuPointer, data.Items, data.Length); - // TODO: use vkMapMemory2 just for consistency - AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset, resourceData->Width, 0, &resourceData->CpuDataPointer)); - } - - auto destinationPointer = (uint8_t*)resourceData->CpuDataPointer + offset; - memcpy(destinationPointer, data.Items, data.Length); + vkUnmapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject); } ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, const ElemDownloadGraphicsBufferDataOptions* options) @@ -556,24 +619,29 @@ ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, con if (resourceData->Type != ElemGraphicsResourceType_Buffer) { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "DownloadGraphicsBufferData only works with graphics buffers."); + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers."); return {}; } - if (resourceData->CpuDataPointer == nullptr) - { - auto resourceDataFull = GetVulkanGraphicsResourceDataFull(resource); - SystemAssert(resourceDataFull); + auto resourceDataFull = GetVulkanGraphicsResourceDataFull(resource); + SystemAssert(resourceDataFull); - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(resourceDataFull->GraphicsDevice); - SystemAssert(graphicsDeviceData); + auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); + SystemAssert(graphicsHeapData); + + if (graphicsHeapData->HeapType != ElemGraphicsHeapType_GpuUpload && graphicsHeapData->HeapType != ElemGraphicsHeapType_Readback) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData only works with graphics buffers allocated in a Readback heap or GpuUpload heap. (%d)", graphicsHeapData->HeapType); + return {}; + } - auto graphicsHeapData = GetVulkanGraphicsHeapData(resourceDataFull->GraphicsHeap); - SystemAssert(graphicsHeapData); + if (graphicsHeapData->HeapType != ElemGraphicsHeapType_Readback) + { + SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "ElemDownloadGraphicsBufferData works faster with graphics buffers allocated in a Readback heap."); + } - // TODO: use vkMapMemory2 just for consistency - AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset, resourceData->Width, 0, &resourceData->CpuDataPointer)); - } + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(resourceDataFull->GraphicsDevice); + SystemAssert(graphicsDeviceData); auto offset = 0u; auto sizeInBytes = resourceData->Width; @@ -590,13 +658,237 @@ ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, con auto stackMemoryArena = SystemGetStackMemoryArena(); auto downloadedData = SystemPushArray(vulkanReadBackMemoryArena, sizeInBytes); - memcpy(downloadedData.Pointer, (uint8_t*)resourceData->CpuDataPointer + offset, sizeInBytes); + + uint8_t* cpuPointer = nullptr; + AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject, resourceDataFull->GraphicsHeapOffset + offset, sizeInBytes, 0, (void**)&cpuPointer)); + + memcpy(downloadedData.Pointer, cpuPointer, sizeInBytes); + + vkUnmapMemory(graphicsDeviceData->Device, graphicsHeapData->DeviceObject); return { .Items = downloadedData.Pointer, .Length = (uint32_t)downloadedData.Length }; } +VulkanUploadBuffer CreateVulkanUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes) +{ + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Create Vulkan UploadBuffer with : %d\n", sizeInBytes); + SystemAssert(graphicsDevice); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); + SystemAssert(graphicsDeviceDataFull); + + VkBufferCreateInfo createInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + createInfo.size = sizeInBytes; + createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + + VkBuffer buffer; + AssertIfFailed(vkCreateBuffer(graphicsDeviceData->Device, &createInfo, nullptr, &buffer)); + + if (VulkanDebugLayerEnabled) + { + VkDebugUtilsObjectNameInfoEXT nameInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT }; + nameInfo.objectType = VK_OBJECT_TYPE_BUFFER; + nameInfo.objectHandle = (uint64_t)buffer; + nameInfo.pObjectName = "ElementalUploadBuffer"; + + AssertIfFailed(vkSetDebugUtilsObjectNameEXT(graphicsDeviceData->Device, &nameInfo)); + } + + VkMemoryAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocateInfo.allocationSize = sizeInBytes; + allocateInfo.memoryTypeIndex = graphicsDeviceDataFull->UploadMemoryTypeIndex; + + VkDeviceMemory deviceMemory; + AssertIfFailed(vkAllocateMemory(graphicsDeviceData->Device, &allocateInfo, nullptr, &deviceMemory)); + AssertIfFailed(vkBindBufferMemory(graphicsDeviceData->Device, buffer, deviceMemory, 0)); + + return + { + .Buffer = buffer, + .DeviceMemory = deviceMemory + }; +} + +UploadBufferMemory GetVulkanUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t alignment, uint64_t sizeInBytes) +{ + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(graphicsDevice); + auto uploadBufferPool = &threadVulkanUploadBufferPools[graphicsIdUnpacked.Index]; + + if (!uploadBufferPool->IsInited) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init pool"); + + auto poolIndex = SystemAtomicAdd(graphicsDeviceData->CurrentUploadBufferPoolIndex, 1); + graphicsDeviceData->UploadBufferPools[poolIndex] = uploadBufferPool; + uploadBufferPool->IsInited = true; + } + + auto uploadBuffer = GetUploadBufferPoolItem(uploadBufferPool, graphicsDeviceData->UploadBufferGeneration, alignment, sizeInBytes); + + if (uploadBuffer.PoolItem->IsResetNeeded) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to create upload buffer with size: %d...", uploadBuffer.PoolItem->SizeInBytes); + + if (uploadBuffer.PoolItem->Fence.FenceValue > 0) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Fence: %d", uploadBuffer.PoolItem->Fence.FenceValue); + VulkanWaitForFenceOnCpu(uploadBuffer.PoolItem->Fence); + } + + if (uploadBuffer.PoolItem->Buffer.Buffer != nullptr) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to delete buffer"); + + vkDestroyBuffer(graphicsDeviceData->Device, uploadBuffer.PoolItem->Buffer.Buffer, nullptr); + vkFreeMemory(graphicsDeviceData->Device, uploadBuffer.PoolItem->Buffer.DeviceMemory, nullptr); + + uploadBuffer.PoolItem->Buffer = {}; + } + + uploadBuffer.PoolItem->Buffer = CreateVulkanUploadBuffer(graphicsDevice, uploadBuffer.PoolItem->SizeInBytes); + uploadBuffer.PoolItem->IsResetNeeded = false; + } + + return uploadBuffer; +} + +void CreateVulkanCopyTextureBarrier(VkCommandBuffer commandBuffer, VkImage image, uint32_t mipLevel, bool beforeCopy) +{ + VkImageMemoryBarrier2 barrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 }; + barrier.image = image; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseMipLevel = mipLevel; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + if (beforeCopy) + { + barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + barrier.srcAccessMask = VK_ACCESS_2_NONE; + barrier.dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + } + else + { + barrier.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT | + VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT; + barrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + + VkDependencyInfo dependencyInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO }; + dependencyInfo.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dependencyInfo.imageMemoryBarrierCount = 1; + dependencyInfo.pImageMemoryBarriers = &barrier; + + vkCmdPipelineBarrier2(commandBuffer, &dependencyInfo); +} + void VulkanCopyDataToGraphicsResource(ElemCommandList commandList, const ElemCopyDataToGraphicsResourceParameters* parameters) { + // TODO: Implement optimizations on the copy queue. On windows, use DirectStorage + auto stackMemoryArena = SystemGetStackMemoryArena(); + + SystemAssert(commandList != ELEM_HANDLE_NULL); + + SystemAssert(parameters); + SystemAssert(parameters->Resource != ELEM_HANDLE_NULL); + + auto commandListData = GetVulkanCommandListData(commandList); + SystemAssert(commandListData); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(commandListData->GraphicsDevice); + SystemAssert(graphicsDeviceData); + + auto resourceData = GetVulkanGraphicsResourceData(parameters->Resource); + SystemAssert(resourceData); + + auto resourceDataFull = GetVulkanGraphicsResourceDataFull(parameters->Resource); + SystemAssert(resourceDataFull); + + ReadOnlySpan sourceData; + + // TODO: Implement file source + if (parameters->SourceType == ElemCopyDataSourceType_Memory) + { + sourceData = ReadOnlySpan(parameters->SourceMemoryData.Items, parameters->SourceMemoryData.Length); + } + + auto uploadBufferAlignment = 4u; + auto uploadBufferSizeInBytes = sourceData.Length; + + auto uploadBuffer = GetVulkanUploadBuffer(commandListData->GraphicsDevice, uploadBufferAlignment, uploadBufferSizeInBytes); + SystemAssert(uploadBuffer.Offset + sourceData.Length <= uploadBuffer.PoolItem->SizeInBytes); + + // TODO: Can we do better here? + auto uploadBufferAlreadyInCommandList = false; + + for (uint32_t i = 0; i < MAX_UPLOAD_BUFFERS; i++) + { + if (commandListData->UploadBufferPoolItems[i] == uploadBuffer.PoolItem) + { + uploadBufferAlreadyInCommandList = true; + break; + } + } + + if (!uploadBufferAlreadyInCommandList) + { + commandListData->UploadBufferPoolItems[commandListData->UploadBufferCount++] = uploadBuffer.PoolItem; + } + + uint8_t* cpuPointer = nullptr; + AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, uploadBuffer.PoolItem->Buffer.DeviceMemory, uploadBuffer.Offset, sourceData.Length, 0, (void**)&cpuPointer)); + + memcpy(cpuPointer, sourceData.Pointer, sourceData.Length); + + if (resourceData->Type == ElemGraphicsResourceType_Buffer) + { + VkBufferCopy copyRegion + { + .srcOffset = uploadBuffer.Offset, + .dstOffset = parameters->BufferOffset, + .size = sourceData.Length + }; + + vkCmdCopyBuffer(commandListData->DeviceObject, uploadBuffer.PoolItem->Buffer.Buffer, resourceData->BufferDeviceObject, 1, ©Region); + } + else if (resourceData->Type == ElemGraphicsResourceType_Texture2D) + { + auto mipLevel = parameters->TextureMipLevel; + auto mipWidth = SystemMax(1u, resourceData->Width >> mipLevel); + auto mipHeight = SystemMax(1u, resourceData->Height >> mipLevel); + + CreateVulkanCopyTextureBarrier(commandListData->DeviceObject, resourceData->TextureDeviceObject, mipLevel, true); + + VkBufferImageCopy region = + { + .bufferOffset = uploadBuffer.Offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = { VK_IMAGE_ASPECT_COLOR_BIT, mipLevel, 0, 1 }, + .imageOffset = { 0, 0, 0 }, + .imageExtent = { mipWidth, mipHeight, 1 }, + }; + + vkCmdCopyBufferToImage(commandListData->DeviceObject, uploadBuffer.PoolItem->Buffer.Buffer, resourceData->TextureDeviceObject, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + CreateVulkanCopyTextureBarrier(commandListData->DeviceObject, resourceData->TextureDeviceObject, mipLevel, false); + } + + vkUnmapMemory(graphicsDeviceData->Device, uploadBuffer.PoolItem->Buffer.DeviceMemory); } ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphicsResource resource, ElemGraphicsResourceDescriptorUsage usage, const ElemGraphicsResourceDescriptorOptions* options) @@ -648,7 +940,7 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi } VkWriteDescriptorSet descriptor = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; - descriptor.dstSet = descriptorHeap.Storage->DescriptorSet; + descriptor.dstSet = descriptorHeap.Storage->DescriptorSet->DescriptorSet; descriptor.dstBinding = 0; descriptor.dstArrayElement = descriptorHandle; descriptor.descriptorCount = 1; @@ -688,6 +980,11 @@ ElemGraphicsResourceDescriptor VulkanCreateGraphicsResourceDescriptor(ElemGraphi vkUpdateDescriptorSets(graphicsDeviceData->Device, 1, &descriptor, 0, nullptr); + if ((descriptorHandle % 1024) == 0) + { + SystemCommitMemory(VulkanGraphicsMemoryArena, &vulkanResourceDescriptorInfos[descriptorHandle], 1024 * sizeof(ElemGraphicsResourceDescriptorInfo)); + } + vulkanResourceDescriptorInfos[descriptorHandle].Resource = resource; vulkanResourceDescriptorInfos[descriptorHandle].Usage = usage; @@ -742,20 +1039,140 @@ void VulkanFreeGraphicsResourceDescriptor(ElemGraphicsResourceDescriptor descrip void VulkanProcessGraphicsResourceDeleteQueue(ElemGraphicsDevice graphicsDevice) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + ProcessResourceDeleteQueue(); SystemClearMemoryArena(vulkanReadBackMemoryArena); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + graphicsDeviceData->UploadBufferGeneration++; + + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + auto uploadBuffersToDelete = GetUploadBufferPoolItemsToDelete(stackMemoryArena, bufferPool, graphicsDeviceData->UploadBufferGeneration); + + for (uint32_t j = 0; j < uploadBuffersToDelete.Length; j++) + { + auto uploadBufferToDelete = uploadBuffersToDelete[j]; + + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Need to purge upload buffer: Size=%d", uploadBufferToDelete->SizeInBytes); + + vkDestroyBuffer(graphicsDeviceData->Device, uploadBufferToDelete->Buffer.Buffer, nullptr); + vkFreeMemory(graphicsDeviceData->Device, uploadBufferToDelete->Buffer.DeviceMemory, nullptr); + + uploadBufferToDelete->Buffer = {}; + + uploadBufferToDelete->CurrentOffset = 0; + uploadBufferToDelete->SizeInBytes = 0; + } + } + } } ElemGraphicsSampler VulkanCreateGraphicsSampler(ElemGraphicsDevice graphicsDevice, const ElemGraphicsSamplerInfo* samplerInfo) { - return {}; + InitVulkanResourceMemory(); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto descriptorHeap = graphicsDeviceData->SamplerDescriptorHeap; + auto descriptorHandle = CreateVulkanDescriptorHandle(descriptorHeap); + + auto localSamplerInfo = *samplerInfo; + + if (localSamplerInfo.MaxAnisotropy == 0) + { + localSamplerInfo.MaxAnisotropy = 1; + } + + auto borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; + + if (localSamplerInfo.BorderColor.Red == 1.0f && localSamplerInfo.BorderColor.Green == 1.0f && localSamplerInfo.BorderColor.Blue == 1.0f && localSamplerInfo.BorderColor.Alpha == 1.0f) + { + borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + } + + VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + samplerCreateInfo.minFilter = ConvertToVulkanFilter(localSamplerInfo.MinFilter); + samplerCreateInfo.magFilter = ConvertToVulkanFilter(localSamplerInfo.MagFilter); + samplerCreateInfo.mipmapMode = ConvertToVulkanSamplerMipMapMode(localSamplerInfo.MipFilter); + samplerCreateInfo.addressModeU = ConvertToVulkanSamplerAddressMode(localSamplerInfo.AddressU); + samplerCreateInfo.addressModeV = ConvertToVulkanSamplerAddressMode(localSamplerInfo.AddressV); + samplerCreateInfo.addressModeW = ConvertToVulkanSamplerAddressMode(localSamplerInfo.AddressW); + samplerCreateInfo.anisotropyEnable = localSamplerInfo.MaxAnisotropy > 1; + samplerCreateInfo.maxAnisotropy = localSamplerInfo.MaxAnisotropy; + samplerCreateInfo.compareEnable = localSamplerInfo.CompareFunction != ElemGraphicsCompareFunction_Never; + samplerCreateInfo.compareOp = ConvertToVulkanCompareFunction(localSamplerInfo.CompareFunction); + samplerCreateInfo.minLod = localSamplerInfo.MinLod; + samplerCreateInfo.maxLod = localSamplerInfo.MaxLod == 0 ? 1000 : localSamplerInfo.MaxLod; + samplerCreateInfo.borderColor = borderColor; + + VkSampler sampler; + AssertIfFailed(vkCreateSampler(graphicsDeviceData->Device, &samplerCreateInfo, 0, &sampler)); + + VkDescriptorImageInfo imageInfo = {}; + imageInfo.sampler = sampler; + + VkWriteDescriptorSet descriptor = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET }; + descriptor.dstSet = descriptorHeap.Storage->DescriptorSet->DescriptorSet; + descriptor.dstBinding = 0; + descriptor.dstArrayElement = descriptorHandle; + descriptor.descriptorCount = 1; + descriptor.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + descriptor.pImageInfo = &imageInfo; + + vkUpdateDescriptorSets(graphicsDeviceData->Device, 1, &descriptor, 0, nullptr); + + if ((descriptorHandle % 1024) == 0) + { + SystemCommitMemory(VulkanGraphicsMemoryArena, &vulkanSamplerInfos[descriptorHandle], 1024 * sizeof(ElemGraphicsSamplerInfo)); + } + + vulkanSamplerInfos[descriptorHandle].GraphicsDevice = graphicsDevice; + vulkanSamplerInfos[descriptorHandle].VulkanSampler = sampler; + vulkanSamplerInfos[descriptorHandle].SamplerInfo = localSamplerInfo; + return descriptorHandle; } ElemGraphicsSamplerInfo VulkanGetGraphicsSamplerInfo(ElemGraphicsSampler sampler) { - return {}; + InitVulkanResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return {}; + } + + return vulkanSamplerInfos[sampler].SamplerInfo; } void VulkanFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphicsSamplerOptions* options) { + InitVulkanResourceMemory(); + + if (sampler == -1) + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Sampler is invalid."); + return; + } + + if (options && options->FencesToWait.Length > 0) + { + EnqueueResourceDeleteEntry(VulkanGraphicsMemoryArena, sampler, ResourceDeleteType_Sampler, options->FencesToWait); + return; + } + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(vulkanSamplerInfos[sampler].GraphicsDevice); + SystemAssert(graphicsDeviceData); + + vkDestroySampler(graphicsDeviceData->Device, vulkanSamplerInfos[sampler].VulkanSampler, nullptr); + vulkanSamplerInfos[sampler] = {}; } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h index bc26c150..26b9b91c 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.h @@ -6,6 +6,7 @@ struct VulkanGraphicsHeapData { VkDeviceMemory DeviceObject; + ElemGraphicsHeapType HeapType; uint64_t SizeInBytes; ElemGraphicsDevice GraphicsDevice; }; @@ -25,7 +26,6 @@ struct VulkanGraphicsResourceData uint32_t Height; uint32_t MipLevels; ElemGraphicsResourceUsage Usage; - void* CpuDataPointer; }; struct VulkanGraphicsResourceDataFull @@ -35,6 +35,19 @@ struct VulkanGraphicsResourceDataFull uint64_t GraphicsHeapOffset; }; +struct VulkanUploadBuffer +{ + VkBuffer Buffer; + VkDeviceMemory DeviceMemory; +}; + +struct VulkanGraphicsSamplerInfo +{ + ElemGraphicsDevice GraphicsDevice; + VkSampler VulkanSampler; + ElemGraphicsSamplerInfo SamplerInfo; +}; + VulkanGraphicsHeapData* GetVulkanGraphicsHeapData(ElemGraphicsHeap graphicsHeap); VulkanGraphicsResourceData* GetVulkanGraphicsResourceData(ElemGraphicsResource resource); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp index ac27a957..5dfefe28 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp @@ -19,7 +19,6 @@ VkPipelineStageFlags2 ConvertToVulkanBarrierSync(ElemGraphicsResourceBarrierSync if (!isDepthStencil) { return VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT | - VK_PIPELINE_STAGE_2_TASK_SHADER_BIT_EXT | VK_PIPELINE_STAGE_2_MESH_SHADER_BIT_EXT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT; } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 965d25b1..330f3c5d 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -169,36 +169,6 @@ VkBlendFactor ConvertToVulkanBlendFactor(ElemGraphicsBlendFactor blendFactor) } } -VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction) -{ - switch (compareFunction) - { - case ElemGraphicsCompareFunction_Never: - return VK_COMPARE_OP_NEVER; - - case ElemGraphicsCompareFunction_Less: - return VK_COMPARE_OP_LESS; - - case ElemGraphicsCompareFunction_Equal: - return VK_COMPARE_OP_EQUAL; - - case ElemGraphicsCompareFunction_LessEqual: - return VK_COMPARE_OP_LESS_OR_EQUAL; - - case ElemGraphicsCompareFunction_Greater: - return VK_COMPARE_OP_GREATER; - - case ElemGraphicsCompareFunction_NotEqual: - return VK_COMPARE_OP_NOT_EQUAL; - - case ElemGraphicsCompareFunction_GreaterEqual: - return VK_COMPARE_OP_GREATER_OR_EQUAL; - - case ElemGraphicsCompareFunction_Always: - return VK_COMPARE_OP_ALWAYS; - } -} - VkPipelineShaderStageCreateInfo GetVulkanShaderFunctionStageCreateInfo(MemoryArena memoryArena, VulkanShaderLibraryData* shaderLibraryData, ShaderType shaderType, const char* function) { SystemAssert(function); @@ -482,8 +452,13 @@ void VulkanBindPipelineState(ElemCommandList commandList, ElemPipelineState pipe auto bindPoint = (pipelineStateData->PipelineStateType == VulkanPipelineStateType_Graphics) ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE; - VkDescriptorSet descriptorSets = { graphicsDeviceData->ResourceDescriptorHeap.Storage->DescriptorSet }; - vkCmdBindDescriptorSets(commandListData->DeviceObject, bindPoint, graphicsDeviceData->PipelineLayout, 0, 1, &descriptorSets, 0, nullptr); + VkDescriptorSet descriptorSets[] = + { + graphicsDeviceData->ResourceDescriptorHeap.Storage->DescriptorSet->DescriptorSet, + graphicsDeviceData->SamplerDescriptorHeap.Storage->DescriptorSet->DescriptorSet + }; + + vkCmdBindDescriptorSets(commandListData->DeviceObject, bindPoint, graphicsDeviceData->PipelineLayout, 0, ARRAYSIZE(descriptorSets), descriptorSets, 0, nullptr); vkCmdBindPipeline(commandListData->DeviceObject, bindPoint, pipelineStateData->PipelineState); } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 99a7ba17..197739c2 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -76,6 +76,8 @@ void CreateVulkanSwapChainBackBuffers(ElemSwapChain swapChain, uint32_t width, u VkSwapchainKHR CreateVulkanSwapChainObject(ElemGraphicsDevice graphicsDevice, VkSurfaceKHR windowSurface, VkSwapchainCreateInfoKHR* swapChainCreateInfo, VkSwapchainKHR oldSwapChain) { + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); SystemAssert(graphicsDeviceData); @@ -85,6 +87,14 @@ VkSwapchainKHR CreateVulkanSwapChainObject(ElemGraphicsDevice graphicsDevice, Vk VkSurfaceCapabilitiesKHR surfaceCapabilities = {}; AssertIfFailed(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(graphicsDeviceDataFull->PhysicalDevice, windowSurface, &surfaceCapabilities)); + uint32_t presentModeCount = 0; + AssertIfFailed(vkGetPhysicalDeviceSurfacePresentModesKHR(graphicsDeviceDataFull->PhysicalDevice, windowSurface, &presentModeCount, nullptr)); + + auto presentModes = SystemPushArray(stackMemoryArena, presentModeCount); + AssertIfFailed(vkGetPhysicalDeviceSurfacePresentModesKHR(graphicsDeviceDataFull->PhysicalDevice, windowSurface, &presentModeCount, presentModes.Pointer)); + + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Present Mode count: %d", presentModeCount); + VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) @@ -143,7 +153,7 @@ void ResizeVulkanSwapChain(ElemSwapChain swapChain, uint32_t width, uint32_t hei auto fence = CreateVulkanCommandQueueFence(swapChainData->CommandQueue); VulkanWaitForFenceOnCpu(fence); - swapChainData->PresentId = 0; + swapChainData->PresentId = 1; auto oldSwapChain = swapChainData->DeviceObject; auto swapChainCreateInfo = &swapChainDataFull->CreateInfo; @@ -192,9 +202,15 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) #ifdef _WIN32 auto presentId = swapChainData->PresentId - swapChainData->FrameLatency; - if (vkWaitForPresentKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, presentId, 0) != VK_SUCCESS) + //if (presentId < 10) + //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "PresentID: %d", presentId); + + //if (vkWaitForPresentKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, presentId, 0) != VK_SUCCESS) { - return; + // BUG: There is still an error here, we need to investigate + // Sometimes when we resize it is good + //SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "Cannot update the swapchain because there was an error waiting for the present ID %d.", presentId); + // return; } #endif } @@ -412,6 +428,7 @@ ElemSwapChain VulkanCreateSwapChain(ElemCommandQueue commandQueue, ElemWindow wi .AspectRatio = (float)width / height, .UIScale = windowRenderSize.UIScale, .Format = ElemGraphicsFormat_B8G8R8A8_SRGB, // TODO: change that + .PresentId = 1, .FrameLatency = frameLatency, .TargetFPS = targetFPS }); @@ -503,9 +520,11 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) auto commandQueueData = GetVulkanCommandQueueData(swapChainData->CommandQueue); SystemAssert(commandQueueData); + auto presentId = swapChainData->PresentId++; + VkPresentIdKHR presentIdInfo = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR }; presentIdInfo.swapchainCount = 1; - presentIdInfo.pPresentIds = &swapChainData->PresentId; + presentIdInfo.pPresentIds = &presentId; VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; presentInfo.waitSemaphoreCount = 1; @@ -519,6 +538,5 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) VulkanResetCommandAllocation(swapChainData->GraphicsDevice); VulkanProcessGraphicsResourceDeleteQueue(swapChainData->GraphicsDevice); - swapChainData->PresentId++; } diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index 5f54f8b1..dfabc7e6 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -489,7 +489,7 @@ typedef enum { ElemGraphicsResourceBarrierSyncType_None, ElemGraphicsResourceBarrierSyncType_Compute, - ElemGraphicsResourceBarrierSyncType_RenderTarget + ElemGraphicsResourceBarrierSyncType_RenderTarget, } ElemGraphicsResourceBarrierSyncType; typedef enum @@ -498,7 +498,7 @@ typedef enum ElemGraphicsResourceBarrierAccessType_Read, ElemGraphicsResourceBarrierAccessType_Write, ElemGraphicsResourceBarrierAccessType_RenderTarget, - ElemGraphicsResourceBarrierAccessType_DepthStencilWrite + ElemGraphicsResourceBarrierAccessType_DepthStencilWrite, } ElemGraphicsResourceBarrierAccessType; typedef enum @@ -831,7 +831,8 @@ typedef struct ElemGraphicsSamplerAddressMode AddressW; uint32_t MaxAnisotropy; ElemGraphicsCompareFunction CompareFunction; - ElemColor BorderColor; + // TODO: Remove this one and do an enum instead + ElemColor BorderColor; float MinLod; float MaxLod; // TODO: Minimum/Maximum filters? diff --git a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp index 9a47d8c6..6d142edb 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12CommandList.cpp @@ -262,6 +262,8 @@ void DirectX12CommitCommandList(ElemCommandList commandList) auto commandListData = GetDirectX12CommandListData(commandList); AssertIfFailed(commandListData->DeviceObject->Close()); + FreeResourceBarrierPool(commandListData->ResourceBarrierPool); + commandListData->IsCommitted = true; threadDirectX12CommandBufferCommitted = true; } @@ -334,7 +336,6 @@ ElemFence DirectX12ExecuteCommandLists(ElemCommandQueue commandQueue, ElemComman } ReleaseCommandListPoolItem(commandListData->CommandListPoolItem); - FreeResourceBarrierPool(commandListData->ResourceBarrierPool); SystemRemoveDataPoolItem(directX12CommandListPool, commandLists.Items[i]); } @@ -360,7 +361,8 @@ void DirectX12WaitForFenceOnCpu(ElemFence fence) if (fence.FenceValue > commandQueueToWaitDataFull->LastCompletedFenceValue) { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for Fence on CPU..."); + // TODO: Activate that message with a debug flag + //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for Fence on CPU..."); commandQueueToWaitDataFull->Fence->SetEventOnCompletion(fence.FenceValue, directX12GlobalFenceEvent); // TODO: It is a little bit extreme to block at infinity. We should set a timeout and break the program. diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Config.h b/src/Elemental/Microsoft/Graphics/DirectX12Config.h index 0c1b2955..94c66542 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Config.h +++ b/src/Elemental/Microsoft/Graphics/DirectX12Config.h @@ -3,7 +3,7 @@ #define D3D12SDK_VERSION 614 #define D3D12SDK_PATH ".\\" -#define DIRECTX12_MEMORY_ARENA 128 * 1024 * 1024 +#define DIRECTX12_MEMORY_ARENA 512 * 1024 * 1024 #define DIRECTX12_READBACK_MEMORY_ARENA 32 * 1024 * 1024 #define DIRECTX12_MAX_DEVICES 10u diff --git a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp index 1adeaea2..56714532 100644 --- a/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp +++ b/src/Elemental/Microsoft/Graphics/DirectX12Resource.cpp @@ -84,7 +84,7 @@ DXGI_FORMAT ConvertDirectX12FormatWithoutSrgbIfNeeded(DXGI_FORMAT format) } } -D3D12_FILTER_TYPE ConvertToDirectX12FilerType(ElemGraphicsSamplerFilter filter) +D3D12_FILTER_TYPE ConvertToDirectX12FilterType(ElemGraphicsSamplerFilter filter) { switch (filter) { @@ -973,9 +973,9 @@ ElemGraphicsResourceDescriptor DirectX12CreateGraphicsResourceDescriptor(ElemGra auto index = ConvertDirectX12DescriptorHandleToIndex(descriptorHeap, descriptorHandle); - if ((index % 1000) == 0) + if ((index % 1024) == 0) { - SystemCommitMemory(DirectX12MemoryArena, &directX12ResourceDescriptorInfos[index], 1000 * sizeof(ElemGraphicsResourceDescriptorInfo)); + SystemCommitMemory(DirectX12MemoryArena, &directX12ResourceDescriptorInfos[index], 1024 * sizeof(ElemGraphicsResourceDescriptorInfo)); } directX12ResourceDescriptorInfos[index].Resource = resource; @@ -1063,9 +1063,9 @@ ElemGraphicsSampler DirectX12CreateGraphicsSampler(ElemGraphicsDevice graphicsDe localSamplerInfo.MaxAnisotropy = 1; } - auto minFilter = ConvertToDirectX12FilerType(localSamplerInfo.MinFilter); - auto magFilter = ConvertToDirectX12FilerType(localSamplerInfo.MagFilter); - auto mipFilter = ConvertToDirectX12FilerType(localSamplerInfo.MipFilter); + auto minFilter = ConvertToDirectX12FilterType(localSamplerInfo.MinFilter); + auto magFilter = ConvertToDirectX12FilterType(localSamplerInfo.MagFilter); + auto mipFilter = ConvertToDirectX12FilterType(localSamplerInfo.MipFilter); auto reduction = localSamplerInfo.CompareFunction == ElemGraphicsCompareFunction_Never ? D3D12_FILTER_REDUCTION_TYPE_STANDARD : D3D12_FILTER_REDUCTION_TYPE_COMPARISON; auto borderColor = localSamplerInfo.BorderColor; auto filter = D3D12_ENCODE_BASIC_FILTER(minFilter, magFilter, mipFilter, reduction); diff --git a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp index efcf68e7..73d53b71 100644 --- a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp @@ -10,9 +10,7 @@ void InitDirectXShaderCompiler() { if (!directXShaderCompilerLibrary.Handle) { - // BUG: For the moment we need to get rid of DXIL.dll in the windows SDK folder to load the correct - // dll on windows - directXShaderCompilerLibrary = SystemLoadLibrary("dxcompiler"); + directXShaderCompilerLibrary = SystemLoadLibrary("./dxcompiler"); if (directXShaderCompilerLibrary.Handle != nullptr) { @@ -41,7 +39,7 @@ bool ProcessDirectXShaderCompilerLogOutput(MemoryArena memoryArena, ComPtr CompileDirectXShader(ReadOnlySpan shaderCode, ReadOn // TODO: Add an options for this // TODO: To review parameters[parameterIndex++] = L"-fspv-use-legacy-buffer-matrix-order"; + + parameters[parameterIndex++] = L"-fvk-bind-resource-heap"; + parameters[parameterIndex++] = L"0"; + parameters[parameterIndex++] = L"0"; + + parameters[parameterIndex++] = L"-fvk-bind-sampler-heap"; + parameters[parameterIndex++] = L"0"; + parameters[parameterIndex++] = L"1"; } parameters[parameterIndex++] = L"-HV"; diff --git a/tests/GraphicsTests/GraphicsTests.cpp b/tests/GraphicsTests/GraphicsTests.cpp index b58120bf..47fa9bef 100644 --- a/tests/GraphicsTests/GraphicsTests.cpp +++ b/tests/GraphicsTests/GraphicsTests.cpp @@ -6,27 +6,49 @@ bool testPrintLogs = true; bool testForceVulkanApi = false; bool workingTestHasLogErrors = false; -char workingTestErrorLogs[TESTLOG_LENGTH]; +char* workingTestErrorLogs; uint32_t currentTestErrorLogsIndex; -char testDebugLogs[TESTLOG_LENGTH]; +char* testDebugLogs; uint32_t currentTestDebugLogsIndex = 0; bool testHasLogErrors = false; -char testErrorLogs[TESTLOG_LENGTH]; +char* testErrorLogs; + +ElemGraphicsOptions startOptions; + +void InitLogBuffers() +{ + workingTestErrorLogs = (char*)malloc(TESTLOG_LENGTH); + testDebugLogs = (char*)malloc(TESTLOG_LENGTH); + testErrorLogs = (char*)malloc(TESTLOG_LENGTH); +} + +void EnableBarriersLog() +{ + ElemGraphicsOptions options = startOptions; + options.EnableDebugBarrierInfo = true; + + ElemSetGraphicsOptions(&options); +} + +void DisableBarriersLog() +{ + ElemGraphicsOptions options = startOptions; + options.EnableDebugBarrierInfo = false; + + ElemSetGraphicsOptions(&options); +} uint64_t TestMegaBytesToBytes(uint64_t value) { return value * 1024 * 1024; } +// TODO: Remove this function void CopyString(char* destination, uint32_t destinationLength, const char* source, uint32_t sourceLength) { - #ifdef _WIN32 - strncpy_s(destination, destinationLength, source, sourceLength); - #else strncpy(destination, source, sourceLength); - #endif } void GetFullPath(char* destination, const char* path) @@ -237,13 +259,16 @@ void TestLogHandler(ElemLogMessageType messageType, ElemLogMessageCategory categ } else if (messageType == ElemLogMessageType_Debug) { - char tmpMessage[TESTLOG_LENGTH]; + char* tmpMessage = (char*)malloc(TESTLOG_LENGTH + 1); snprintf(tmpMessage, TESTLOG_LENGTH, "%s\n", message); auto tmpMessageLength = strlen(tmpMessage); char* logCopyDestination = testDebugLogs + currentTestDebugLogsIndex; + //strncpy(logCopyDestination, tmpMessage, tmpMessageLength + 1); CopyString(logCopyDestination, TESTLOG_LENGTH - currentTestDebugLogsIndex, tmpMessage, tmpMessageLength + 1); currentTestDebugLogsIndex += tmpMessageLength; + + free(tmpMessage); } } @@ -326,9 +351,9 @@ TestGpuBuffer TestCreateGpuBuffer(ElemGraphicsDevice graphicsDevice, uint32_t si .HeapType = heapType }; - auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, sizeInBytes, &heapOptions); - auto gpuBufferInfo = ElemCreateGraphicsBufferResourceInfo(graphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Write, nullptr); + + auto graphicsHeap = ElemCreateGraphicsHeap(graphicsDevice, gpuBufferInfo.SizeInBytes, &heapOptions); auto gpuBuffer = ElemCreateGraphicsResource(graphicsHeap, 0, &gpuBufferInfo); auto gpuBufferReadDescriptor = ElemCreateGraphicsResourceDescriptor(gpuBuffer, ElemGraphicsResourceDescriptorUsage_Read, nullptr); auto gpuBufferWriteDescriptor = ElemCreateGraphicsResourceDescriptor(gpuBuffer, ElemGraphicsResourceDescriptorUsage_Write, nullptr); diff --git a/tests/GraphicsTests/GraphicsTests.h b/tests/GraphicsTests/GraphicsTests.h index 0cc66423..4d47d221 100644 --- a/tests/GraphicsTests/GraphicsTests.h +++ b/tests/GraphicsTests/GraphicsTests.h @@ -4,13 +4,19 @@ #include #include -#define TESTLOG_LENGTH 4096 +#define TESTLOG_LENGTH 4 * 1024 * 1024 #ifndef _WIN32 #define MAX_PATH 255 #define ARRAYSIZE(a) (sizeof(a) / sizeof(*(a))) #endif +extern ElemGraphicsOptions startOptions; + +void InitLogBuffers(); +void EnableBarriersLog(); +void DisableBarriersLog(); + #define Max(a,b) ((a) > (b) ? (a) : (b)) #define Min(a,b) ((a) < (b) ? (a) : (b)) @@ -169,7 +175,7 @@ struct TestBarrierCheck extern bool testPrintLogs; extern bool testForceVulkanApi; extern bool testHasLogErrors; -extern char testErrorLogs[TESTLOG_LENGTH]; +extern char* testErrorLogs; extern uint32_t currentTestErrorLogsIndex; uint64_t TestMegaBytesToBytes(uint64_t value); diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index d7749098..f94e41b0 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -57,7 +57,6 @@ UTEST(ResourceIO, UploadGraphicsBufferData_WithNoGpuUploadHeap) auto resource = ElemCreateGraphicsResource(graphicsHeap, 0, &resourceInfo); uint8_t data[] = { 1, 2, 3, 4 }; - // Act ElemUploadGraphicsBufferData(resource, 0, { .Items = data, .Length = ARRAYSIZE(data) }); @@ -296,6 +295,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBuffer) UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopies) { // Arrange + DisableBarriersLog(); auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); ElemGraphicsHeapOptions options = @@ -328,7 +328,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopies) .Resource = resource, .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), .SourceType = ElemCopyDataSourceType_Memory, - .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = sizeof(uint32_t) } }; ElemCopyDataToGraphicsResource(commandList, ¶meters); @@ -357,11 +357,13 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopies) } free(data); + EnableBarriersLog(); } UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandLists) { // Arrange + DisableBarriersLog(); auto graphicsDevice = ElemCreateGraphicsDevice(nullptr); ElemGraphicsHeapOptions options = @@ -397,7 +399,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandList .Resource = resource, .BufferOffset = (uint32_t)(i * sizeof(uint32_t)), .SourceType = ElemCopyDataSourceType_Memory, - .SourceMemoryData = { .Items = (uint8_t*)data, .Length = (uint32_t)(resourceSize - i * sizeof(uint32_t)) } + .SourceMemoryData = { .Items = (uint8_t*)data, .Length = sizeof(uint32_t) } }; ElemCopyDataToGraphicsResource(commandList, ¶meters); @@ -427,6 +429,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithBufferMultiCopiesAndCommandList } free(data); + EnableBarriersLog(); } UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) @@ -516,7 +519,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) for (uint32_t j = 0; j < outputMipDataItem.Length; j++) { - ASSERT_EQ_MSG(outputMipDataItem.Items[j], mipData[i][j], "Test"); + ASSERT_EQ_MSG(outputMipDataItem.Items[j], mipData[i][j], "TestMipData"); } free(mipData[i]); diff --git a/tests/GraphicsTests/UnityBuild.cpp b/tests/GraphicsTests/UnityBuild.cpp index db1e05ee..1d3e265d 100644 --- a/tests/GraphicsTests/UnityBuild.cpp +++ b/tests/GraphicsTests/UnityBuild.cpp @@ -30,8 +30,11 @@ void ApplicationTestInitFunction(void* payload) options.EnableGpuValidation = false; options.EnableDebugBarrierInfo = true; + startOptions = options; + ElemConfigureLogHandler(TestLogHandler); ElemSetGraphicsOptions(&options); + InitLogBuffers(); auto result = utest_main(applicationTestPayload->argc, applicationTestPayload->argv); From b0c862ca95e1fdec40ead6f3f26f65631c66080f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 18 Jan 2025 05:59:43 +0100 Subject: [PATCH 91/93] Refactor project --- .../01-Renderer}/CMakeLists.txt | 0 .../01-Renderer}/Data/RenderMesh.hlsl | 0 .../99-Renderer => Demos/01-Renderer}/main.c | 0 .../CMakeLists.txt | 0 .../Data/Triangle.hlsl | 0 .../main.c | 0 .../Elemental/01-HelloWindow/CMakeLists.txt | 7 - samples/Elemental/01-HelloWindow/main.c | 36 -- .../CMakeLists.txt | 0 .../Data/Triangle.hlsl | 0 .../{03-HelloInputs => 02-HelloInputs}/main.c | 0 .../CMakeLists.txt | 0 .../Data/Fractal.hlsl | 0 .../Data/Tonemap.hlsl | 0 .../main.c | 0 .../CMakeLists.txt | 0 .../Data/RenderMesh.hlsl | 3 +- .../{06-HelloMesh => 04-HelloMesh}/main.c | 0 .../Elemental/05-HelloImGui/CMakeLists.txt | 10 - .../05-HelloImGui/Data/RenderImGui.hlsl | 83 ----- samples/Elemental/05-HelloImGui/main.c | 313 ------------------ .../Common/Graphics/UploadBufferPool.cpp | 2 +- .../Graphics/Vulkan/VulkanCommandList.cpp | 4 +- .../Common/Graphics/Vulkan/VulkanConfig.h | 21 ++ .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 3 +- .../Graphics/Vulkan/VulkanGraphicsDevice.h | 7 - .../Common/Graphics/Vulkan/VulkanResource.cpp | 25 +- .../Common/Graphics/Vulkan/VulkanShader.cpp | 4 +- .../Graphics/Vulkan/VulkanSwapChain.cpp | 5 +- .../Common/Graphics/Vulkan/VulkanSwapChain.h | 3 +- tests/GraphicsTests/Data/Assert.hlsl | 10 +- tests/GraphicsTests/ResourceIOTests.cpp | 1 + 32 files changed, 53 insertions(+), 484 deletions(-) rename samples/{Elemental/99-Renderer => Demos/01-Renderer}/CMakeLists.txt (100%) rename samples/{Elemental/99-Renderer => Demos/01-Renderer}/Data/RenderMesh.hlsl (100%) rename samples/{Elemental/99-Renderer => Demos/01-Renderer}/main.c (100%) rename samples/Elemental/{02-HelloTriangle => 01-HelloTriangle}/CMakeLists.txt (100%) rename samples/Elemental/{02-HelloTriangle => 01-HelloTriangle}/Data/Triangle.hlsl (100%) rename samples/Elemental/{02-HelloTriangle => 01-HelloTriangle}/main.c (100%) delete mode 100644 samples/Elemental/01-HelloWindow/CMakeLists.txt delete mode 100644 samples/Elemental/01-HelloWindow/main.c rename samples/Elemental/{03-HelloInputs => 02-HelloInputs}/CMakeLists.txt (100%) rename samples/Elemental/{03-HelloInputs => 02-HelloInputs}/Data/Triangle.hlsl (100%) rename samples/Elemental/{03-HelloInputs => 02-HelloInputs}/main.c (100%) rename samples/Elemental/{04-HelloCompute => 03-HelloCompute}/CMakeLists.txt (100%) rename samples/Elemental/{04-HelloCompute => 03-HelloCompute}/Data/Fractal.hlsl (100%) rename samples/Elemental/{04-HelloCompute => 03-HelloCompute}/Data/Tonemap.hlsl (100%) rename samples/Elemental/{04-HelloCompute => 03-HelloCompute}/main.c (100%) rename samples/Elemental/{06-HelloMesh => 04-HelloMesh}/CMakeLists.txt (100%) rename samples/Elemental/{06-HelloMesh => 04-HelloMesh}/Data/RenderMesh.hlsl (97%) rename samples/Elemental/{06-HelloMesh => 04-HelloMesh}/main.c (100%) delete mode 100644 samples/Elemental/05-HelloImGui/CMakeLists.txt delete mode 100644 samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl delete mode 100644 samples/Elemental/05-HelloImGui/main.c create mode 100644 src/Elemental/Common/Graphics/Vulkan/VulkanConfig.h diff --git a/samples/Elemental/99-Renderer/CMakeLists.txt b/samples/Demos/01-Renderer/CMakeLists.txt similarity index 100% rename from samples/Elemental/99-Renderer/CMakeLists.txt rename to samples/Demos/01-Renderer/CMakeLists.txt diff --git a/samples/Elemental/99-Renderer/Data/RenderMesh.hlsl b/samples/Demos/01-Renderer/Data/RenderMesh.hlsl similarity index 100% rename from samples/Elemental/99-Renderer/Data/RenderMesh.hlsl rename to samples/Demos/01-Renderer/Data/RenderMesh.hlsl diff --git a/samples/Elemental/99-Renderer/main.c b/samples/Demos/01-Renderer/main.c similarity index 100% rename from samples/Elemental/99-Renderer/main.c rename to samples/Demos/01-Renderer/main.c diff --git a/samples/Elemental/02-HelloTriangle/CMakeLists.txt b/samples/Elemental/01-HelloTriangle/CMakeLists.txt similarity index 100% rename from samples/Elemental/02-HelloTriangle/CMakeLists.txt rename to samples/Elemental/01-HelloTriangle/CMakeLists.txt diff --git a/samples/Elemental/02-HelloTriangle/Data/Triangle.hlsl b/samples/Elemental/01-HelloTriangle/Data/Triangle.hlsl similarity index 100% rename from samples/Elemental/02-HelloTriangle/Data/Triangle.hlsl rename to samples/Elemental/01-HelloTriangle/Data/Triangle.hlsl diff --git a/samples/Elemental/02-HelloTriangle/main.c b/samples/Elemental/01-HelloTriangle/main.c similarity index 100% rename from samples/Elemental/02-HelloTriangle/main.c rename to samples/Elemental/01-HelloTriangle/main.c diff --git a/samples/Elemental/01-HelloWindow/CMakeLists.txt b/samples/Elemental/01-HelloWindow/CMakeLists.txt deleted file mode 100644 index 3f17b313..00000000 --- a/samples/Elemental/01-HelloWindow/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -set(SAMPLE_NAME HelloWindow) - -add_executable(${SAMPLE_NAME} main.c) -target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) -add_dependencies(${SAMPLE_NAME} Elemental) - -configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental) diff --git a/samples/Elemental/01-HelloWindow/main.c b/samples/Elemental/01-HelloWindow/main.c deleted file mode 100644 index 1e60c2e4..00000000 --- a/samples/Elemental/01-HelloWindow/main.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "Elemental.h" - -typedef struct -{ - ElemWindow Window; -} ApplicationPayload; - -void InitSample(void* payload) -{ - ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - - ElemWindow window = ElemCreateWindow(NULL); - applicationPayload->Window = window; -} - -void FreeSample(void* payload) -{ - ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - ElemFreeWindow(applicationPayload->Window); - printf("Exit Sample\n"); -} - -int main(void) -{ - ElemConfigureLogHandler(ElemConsoleLogHandler); - - ApplicationPayload payload = {0}; - - ElemRunApplication(&(ElemRunApplicationParameters) - { - .ApplicationName = "Hello Window", - .InitHandler = InitSample, - .FreeHandler = FreeSample, - .Payload = &payload - }); -} diff --git a/samples/Elemental/03-HelloInputs/CMakeLists.txt b/samples/Elemental/02-HelloInputs/CMakeLists.txt similarity index 100% rename from samples/Elemental/03-HelloInputs/CMakeLists.txt rename to samples/Elemental/02-HelloInputs/CMakeLists.txt diff --git a/samples/Elemental/03-HelloInputs/Data/Triangle.hlsl b/samples/Elemental/02-HelloInputs/Data/Triangle.hlsl similarity index 100% rename from samples/Elemental/03-HelloInputs/Data/Triangle.hlsl rename to samples/Elemental/02-HelloInputs/Data/Triangle.hlsl diff --git a/samples/Elemental/03-HelloInputs/main.c b/samples/Elemental/02-HelloInputs/main.c similarity index 100% rename from samples/Elemental/03-HelloInputs/main.c rename to samples/Elemental/02-HelloInputs/main.c diff --git a/samples/Elemental/04-HelloCompute/CMakeLists.txt b/samples/Elemental/03-HelloCompute/CMakeLists.txt similarity index 100% rename from samples/Elemental/04-HelloCompute/CMakeLists.txt rename to samples/Elemental/03-HelloCompute/CMakeLists.txt diff --git a/samples/Elemental/04-HelloCompute/Data/Fractal.hlsl b/samples/Elemental/03-HelloCompute/Data/Fractal.hlsl similarity index 100% rename from samples/Elemental/04-HelloCompute/Data/Fractal.hlsl rename to samples/Elemental/03-HelloCompute/Data/Fractal.hlsl diff --git a/samples/Elemental/04-HelloCompute/Data/Tonemap.hlsl b/samples/Elemental/03-HelloCompute/Data/Tonemap.hlsl similarity index 100% rename from samples/Elemental/04-HelloCompute/Data/Tonemap.hlsl rename to samples/Elemental/03-HelloCompute/Data/Tonemap.hlsl diff --git a/samples/Elemental/04-HelloCompute/main.c b/samples/Elemental/03-HelloCompute/main.c similarity index 100% rename from samples/Elemental/04-HelloCompute/main.c rename to samples/Elemental/03-HelloCompute/main.c diff --git a/samples/Elemental/06-HelloMesh/CMakeLists.txt b/samples/Elemental/04-HelloMesh/CMakeLists.txt similarity index 100% rename from samples/Elemental/06-HelloMesh/CMakeLists.txt rename to samples/Elemental/04-HelloMesh/CMakeLists.txt diff --git a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl b/samples/Elemental/04-HelloMesh/Data/RenderMesh.hlsl similarity index 97% rename from samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl rename to samples/Elemental/04-HelloMesh/Data/RenderMesh.hlsl index 37a33c5f..ef91d4e3 100644 --- a/samples/Elemental/06-HelloMesh/Data/RenderMesh.hlsl +++ b/samples/Elemental/04-HelloMesh/Data/RenderMesh.hlsl @@ -32,6 +32,7 @@ struct Vertex { float3 Position; float3 Normal; + float4 Tangent; float2 TextureCoordinates; }; @@ -114,7 +115,7 @@ void MeshMain(in uint groupId: SV_GroupID, Vertex vertex = meshBuffer.Load(parameters.VertexBufferOffset + vertexIndex * sizeof(Vertex)); vertices[groupThreadId].Position = mul(float4(vertex.Position, 1.0), worldViewProjectionMatrix); - vertices[groupThreadId].WorldNormal = mul(vertex.Normal, inverseTransposeWorldMatrix); // TODO: Compute inverse transpose + vertices[groupThreadId].WorldNormal = mul(float4(vertex.Normal, 0.0), inverseTransposeWorldMatrix).xyz; // TODO: Compute inverse transpose vertices[groupThreadId].MeshletIndex = groupId; } diff --git a/samples/Elemental/06-HelloMesh/main.c b/samples/Elemental/04-HelloMesh/main.c similarity index 100% rename from samples/Elemental/06-HelloMesh/main.c rename to samples/Elemental/04-HelloMesh/main.c diff --git a/samples/Elemental/05-HelloImGui/CMakeLists.txt b/samples/Elemental/05-HelloImGui/CMakeLists.txt deleted file mode 100644 index d83797ab..00000000 --- a/samples/Elemental/05-HelloImGui/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SAMPLE_NAME HelloImGui) - -add_executable(${SAMPLE_NAME} main.c Data) -target_link_libraries(${SAMPLE_NAME} PRIVATE SampleCommon) -target_link_libraries(${SAMPLE_NAME} PUBLIC cimgui) -target_link_libraries(${SAMPLE_NAME} PRIVATE ElementalInterface) -add_dependencies(${SAMPLE_NAME} SampleSharedData) - -configure_resource_compilation(${SAMPLE_NAME} resource_list) -configure_project_package(${SAMPLE_NAME} "samples" DEPENDENCIES Elemental RESOURCES ${resource_list}) diff --git a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl b/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl deleted file mode 100644 index f55aa7ef..00000000 --- a/samples/Elemental/05-HelloImGui/Data/RenderImGui.hlsl +++ /dev/null @@ -1,83 +0,0 @@ -struct ShaderParameters -{ - uint VertexBufferIndex; - uint IndexBufferIndex; - uint VertexOffset; - uint IndexOffset; - uint IndexCount; - uint RenderWidth; - uint RenderHeight; -}; - -[[vk::push_constant]] -ShaderParameters parameters : register(b0); - -struct ImDrawVert -{ - float2 Position; - float2 TextureCoordinates; - uint Color; -}; - -struct VertexOutput -{ - float4 Position: SV_Position; - float2 TextureCoordinates: TEXCOORD0; - float4 Color: COLOR0; -}; - -float4x4 CreateOrthographicMatrixOffCenter(float minPlaneX, float maxPlaneX, float minPlaneY, float maxPlaneY) -{ - float4 row1 = float4(2.0f / (maxPlaneX - minPlaneX), 0.0f, 0.0f, 0.0f); - float4 row2 = float4(0.0f, 2.0f / (minPlaneY - maxPlaneY), 0.0f, 0.0f); - float4 row3 = float4(0.0f, 0.0f, 0.5, 1.0f); - float4 row4 = float4((minPlaneX + maxPlaneX) / (minPlaneX - maxPlaneX), (minPlaneY + maxPlaneY) / (maxPlaneY - minPlaneY), 0.5, 1.0f); - - return float4x4(row1, row2, row3, row4); -} - -#define MAX_VERTEX_COUNT 126 - -[shader("mesh")] -[OutputTopology("triangle")] -[NumThreads(MAX_VERTEX_COUNT, 1, 1)] -void MeshMain(in uint groupId: SV_GroupID, - in uint groupThreadId : SV_GroupThreadID, - out vertices VertexOutput vertices[MAX_VERTEX_COUNT], - out indices uint3 indices[MAX_VERTEX_COUNT / 3]) -{ - // TODO: For the moment we process several time the same vertices because we don't have - // unique indices for the meshlet - uint vertexCount = min(MAX_VERTEX_COUNT, parameters.IndexCount - groupId * MAX_VERTEX_COUNT); - uint triangleCount = vertexCount / 3; - SetMeshOutputCounts(vertexCount, triangleCount); - - if (groupThreadId < vertexCount) - { - float4x4 projectionMatrix = CreateOrthographicMatrixOffCenter(0, parameters.RenderWidth, 0, parameters.RenderHeight); - - ByteAddressBuffer indexBuffer = ResourceDescriptorHeap[parameters.IndexBufferIndex]; - ByteAddressBuffer vertexBuffer = ResourceDescriptorHeap[parameters.VertexBufferIndex]; - - uint16_t vertexIndex = indexBuffer.Load((parameters.IndexOffset + groupId * MAX_VERTEX_COUNT + groupThreadId) * sizeof(uint16_t)); - ImDrawVert vertex = vertexBuffer.Load((parameters.VertexOffset + vertexIndex) * sizeof(ImDrawVert)); - - float4 color = unpack_u8u32(vertex.Color) / 255.0; - color = float4(pow(color.rgb, 2.2f), color.a); - - vertices[groupThreadId].Position = mul(float4(vertex.Position.x, vertex.Position.y, 0.0, 1.0), projectionMatrix); - vertices[groupThreadId].TextureCoordinates = vertex.TextureCoordinates; - vertices[groupThreadId].Color = color; - } - - if (groupThreadId < triangleCount) - { - indices[groupThreadId] = uint3(groupThreadId * 3, groupThreadId * 3 + 1, groupThreadId * 3 + 2); - } -} - -[shader("pixel")] -float4 PixelMain(const VertexOutput input) : SV_Target0 -{ - return input.Color; -} diff --git a/samples/Elemental/05-HelloImGui/main.c b/samples/Elemental/05-HelloImGui/main.c deleted file mode 100644 index 023d889b..00000000 --- a/samples/Elemental/05-HelloImGui/main.c +++ /dev/null @@ -1,313 +0,0 @@ -#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS -#include -#include "Elemental.h" -#include "SampleUtils.h" - -typedef struct -{ - uint32_t FontTextureId; - ElemGraphicsResource VertexBuffer; - ElemGraphicsResourceDescriptor VertexBufferReadDescriptor; - ElemGraphicsResource IndexBuffer; - ElemGraphicsResourceDescriptor IndexBufferReadDescriptor; - ElemPipelineState RenderPipeline; -} ElemImGuiBackendData; - -typedef struct -{ - uint32_t VertexBufferIndex; - uint32_t IndexBufferIndex; - uint32_t VertexOffset; - uint32_t IndexOffset; - uint32_t IndexCount; - uint32_t RenderWidth; - uint32_t RenderHeight; -} ImGuiShaderParameters; - -typedef struct -{ - bool PreferVulkan; - bool ShowImGuiDemoWindow; - ElemWindow Window; - ElemGraphicsDevice GraphicsDevice; - ElemCommandQueue CommandQueue; - ElemGraphicsHeap GraphicsHeap; - uint32_t CurrentHeapOffset; - ElemFence LastExecutionFence; - ElemSwapChain SwapChain; - ElemImGuiBackendData ImGuiBackendData; -} ApplicationPayload; - -void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload); - -void CreateBuffer(ElemGraphicsResource* buffer, ElemGraphicsResourceDescriptor* readDescriptor, ApplicationPayload* applicationPayload, uint32_t sizeInBytes) -{ - // TODO: Alignment should be used with the offset before adding the size of the resource! - ElemGraphicsResourceInfo bufferDescription = ElemCreateGraphicsBufferResourceInfo(applicationPayload->GraphicsDevice, sizeInBytes, ElemGraphicsResourceUsage_Read, NULL); - - applicationPayload->CurrentHeapOffset = SampleAlignValue(applicationPayload->CurrentHeapOffset, bufferDescription.Alignment); - *buffer = ElemCreateGraphicsResource(applicationPayload->GraphicsHeap, applicationPayload->CurrentHeapOffset, &bufferDescription); - applicationPayload->CurrentHeapOffset += bufferDescription.SizeInBytes; - - *readDescriptor = ElemCreateGraphicsResourceDescriptor(*buffer, ElemGraphicsResourceDescriptorUsage_Read, NULL); -} - -void ImGuiInitBackend(ApplicationPayload* payload) -{ - ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; - - ImGuiIO* imGuiIO = igGetIO(); - imGuiIO->BackendRendererUserData = &payload->ImGuiBackendData; - imGuiIO->BackendRendererName = "Elemental"; - imGuiIO->BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; - //imGuiIO->ConfigFlags |= ImGuiConfigFlags_IsSRGB; - - // TODO: Dynamically manage heaps and buffers - CreateBuffer(&imGuiData->VertexBuffer, &imGuiData->VertexBufferReadDescriptor, payload, 20000 * sizeof(ImDrawVert)); - CreateBuffer(&imGuiData->IndexBuffer, &imGuiData->IndexBufferReadDescriptor, payload, 20000 * sizeof(ImDrawIdx)); - - // TODO: Font - uint8_t* fontPixels; - int32_t fontWidth; - int32_t fontHeight; - int32_t fontBytesPerPixel; - ImFontAtlas_GetTexDataAsRGBA32(imGuiIO->Fonts, &fontPixels, &fontWidth, &fontHeight, &fontBytesPerPixel); - - payload->ImGuiBackendData.FontTextureId = 1; - imGuiIO->Fonts->TexID = (ImTextureID)&imGuiData->FontTextureId; - - ElemSwapChainInfo swapChainInfo = ElemGetSwapChainInfo(payload->SwapChain); - - ElemDataSpan shaderData = SampleReadFile(!payload->PreferVulkan ? "RenderImGui.shader": "RenderImGui_vulkan.shader", true); - ElemShaderLibrary shaderLibrary = ElemCreateShaderLibrary(payload->GraphicsDevice, shaderData); - - imGuiData->RenderPipeline = ElemCompileGraphicsPipelineState(payload->GraphicsDevice, &(ElemGraphicsPipelineStateParameters) { - .DebugName = "RenderImGui PSO", - .ShaderLibrary = shaderLibrary, - .MeshShaderFunction = "MeshMain", - .PixelShaderFunction = "PixelMain", - .CullMode = ElemGraphicsCullMode_None, - .RenderTargets = - { - .Items = (ElemGraphicsPipelineStateRenderTarget[]) { - { - .Format = swapChainInfo.Format, - .BlendOperation = ElemGraphicsBlendOperation_Add, - .SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlpha, - .DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, - .BlendOperationAlpha = ElemGraphicsBlendOperation_Add, - .SourceBlendFactorAlpha = ElemGraphicsBlendFactor_One, - .DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_InverseSourceAlpha - }}, - .Length = 1 - }, - }); - - ElemFreeShaderLibrary(shaderLibrary); -} - -void ImGuidElementalNewFrame(const ElemSwapChainUpdateParameters* updateParameters, const ElemInputStream inputStream) -{ - // TODO: Investigate: https://github.com/ocornut/imgui/releases/tag/v1.91.1 - //ImGuiPlatformIO* imGuiPlatformIO = igGetPlatformIO(); - - ImGuiIO* imGuiIO = igGetIO(); - - imGuiIO->DisplaySize = (ImVec2) - { - (float)updateParameters->SwapChainInfo.Width / updateParameters->SwapChainInfo.UIScale, - (float)updateParameters->SwapChainInfo.Height / updateParameters->SwapChainInfo.UIScale - }; - - imGuiIO->DisplayFramebufferScale = (ImVec2) { updateParameters->SwapChainInfo.UIScale, updateParameters->SwapChainInfo.UIScale }; - imGuiIO->DeltaTime = updateParameters->DeltaTimeInSeconds; - - for (uint32_t i = 0; i < inputStream.Events.Length; i++) - { - ElemInputEvent* inputEvent = &inputStream.Events.Items[i]; - - if (inputEvent->InputId == ElemInputId_MouseAxisXNegative || - inputEvent->InputId == ElemInputId_MouseAxisXPositive || - inputEvent->InputId == ElemInputId_MouseAxisYNegative || - inputEvent->InputId == ElemInputId_MouseAxisYPositive) - { - ElemWindowCursorPosition cursorPosition = ElemGetWindowCursorPosition(updateParameters->SwapChainInfo.Window); - ImGuiIO_AddMousePosEvent(imGuiIO, cursorPosition.X / updateParameters->SwapChainInfo.UIScale, cursorPosition.Y / updateParameters->SwapChainInfo.UIScale); - } - - if (inputEvent->InputId == ElemInputId_MouseLeftButton) - { - ImGuiIO_AddMouseButtonEvent(imGuiIO, ImGuiMouseButton_Left, inputEvent->Value); - } - - if (inputEvent->InputId == ElemInputId_MouseWheelNegative || inputEvent->InputId == ElemInputId_MouseWheelPositive) - { - // TODO: Input wheel should maybe normalized to one? - ImGuiIO_AddMouseWheelEvent(imGuiIO, 0.0f, (inputEvent->InputId == ElemInputId_MouseWheelPositive ? inputEvent->Value : -inputEvent->Value) / 10.0f); - } - } - - //imGuiIO->ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts | ImGuiConfigFlags_DpiEnableScaleViewports; -} - -void ImGuiRenderDrawData(ImDrawData* drawData, ElemCommandList commandList, ApplicationPayload* payload, const ElemSwapChainUpdateParameters* updateParameters) -{ - ElemImGuiBackendData* imGuiData = &payload->ImGuiBackendData; - - uint32_t currentVertexOffset = 0; - uint32_t currentIndexOffset = 0; - - for (int32_t i = 0; i < drawData->CmdListsCount; i++) - { - ImDrawList* imCommandList = drawData->CmdLists.Data[i]; - - for (int32_t j = 0; j < imCommandList->CmdBuffer.Size; j++) - { - ImDrawCmd* drawCommand = &imCommandList->CmdBuffer.Data[j]; - - ImGuiShaderParameters shaderParameters = - { - .VertexBufferIndex = imGuiData->VertexBufferReadDescriptor, - .IndexBufferIndex = imGuiData->IndexBufferReadDescriptor, - .VertexOffset = currentVertexOffset + drawCommand->VtxOffset, - .IndexOffset = currentIndexOffset + drawCommand->IdxOffset, - .IndexCount = drawCommand->ElemCount, - .RenderWidth = drawData->DisplaySize.x, - .RenderHeight = drawData->DisplaySize.y - }; - - ElemBindPipelineState(commandList, imGuiData->RenderPipeline); - ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&shaderParameters, .Length = sizeof(ImGuiShaderParameters) }); - ElemSetScissorRectangle(commandList, &(ElemRectangle) { - .X = drawCommand->ClipRect.x * updateParameters->SwapChainInfo.UIScale, - .Y = drawCommand->ClipRect.y * updateParameters->SwapChainInfo.UIScale, - .Width = (drawCommand->ClipRect.z - drawCommand->ClipRect.x) * updateParameters->SwapChainInfo.UIScale, - .Height = (drawCommand->ClipRect.w - drawCommand->ClipRect.y) * updateParameters->SwapChainInfo.UIScale - }); - - uint32_t threadSize = 126; - uint32_t dispatchCount = (drawCommand->ElemCount + (threadSize - 1)) / threadSize; - ElemDispatchMesh(commandList, dispatchCount, 1, 1); - } - - ElemUploadGraphicsBufferData(imGuiData->VertexBuffer, currentVertexOffset * sizeof(ImDrawVert), (ElemDataSpan) { - .Items = (uint8_t*)imCommandList->VtxBuffer.Data, - .Length = imCommandList->VtxBuffer.Size * sizeof(ImDrawVert) - }); - - currentVertexOffset += imCommandList->VtxBuffer.Size; - - ElemUploadGraphicsBufferData(imGuiData->IndexBuffer, currentIndexOffset * sizeof(ImDrawIdx), (ElemDataSpan) { - .Items = (uint8_t*)imCommandList->IdxBuffer.Data, - .Length = imCommandList->IdxBuffer.Size * sizeof(ImDrawIdx) - }); - - currentIndexOffset += imCommandList->IdxBuffer.Size; - } -} - -void InitSample(void* payload) -{ - ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - applicationPayload->Window = ElemCreateWindow(NULL); - - ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = true, .EnableGpuValidation = false, .EnableDebugBarrierInfo = false, .PreferVulkan = applicationPayload->PreferVulkan }); - applicationPayload->GraphicsDevice = ElemCreateGraphicsDevice(NULL); - - applicationPayload->CommandQueue= ElemCreateCommandQueue(applicationPayload->GraphicsDevice, ElemCommandQueueType_Graphics, NULL); - applicationPayload->SwapChain= ElemCreateSwapChain(applicationPayload->CommandQueue, applicationPayload->Window, UpdateSwapChain, &(ElemSwapChainOptions) { .FrameLatency = 1, .UpdatePayload = payload }); - - applicationPayload->GraphicsHeap = ElemCreateGraphicsHeap(applicationPayload->GraphicsDevice, SampleMegaBytesToBytes(64), &(ElemGraphicsHeapOptions) { .HeapType = ElemGraphicsHeapType_GpuUpload }); - - SampleStartFrameMeasurement(); - - igCreateContext(NULL); - ImGuiInitBackend(applicationPayload); -} - -void FreeSample(void* payload) -{ - ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - - ElemWaitForFenceOnCpu(applicationPayload->LastExecutionFence); - - ElemFreePipelineState(applicationPayload->ImGuiBackendData.RenderPipeline); - ElemFreeGraphicsResource(applicationPayload->ImGuiBackendData.VertexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(applicationPayload->ImGuiBackendData.VertexBufferReadDescriptor, NULL); - ElemFreeGraphicsResource(applicationPayload->ImGuiBackendData.IndexBuffer, NULL); - ElemFreeGraphicsResourceDescriptor(applicationPayload->ImGuiBackendData.IndexBufferReadDescriptor, NULL); - - ElemFreeSwapChain(applicationPayload->SwapChain); - ElemFreeCommandQueue(applicationPayload->CommandQueue); - - ElemFreeGraphicsHeap(applicationPayload->GraphicsHeap); - ElemFreeGraphicsDevice(applicationPayload->GraphicsDevice); -} - -void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void* payload) -{ - ApplicationPayload* applicationPayload = (ApplicationPayload*)payload; - - ElemInputStream inputStream = ElemGetInputStream(); - - ElemCommandList commandList = ElemGetCommandList(applicationPayload->CommandQueue, NULL); - - ElemBeginRenderPass(commandList, &(ElemBeginRenderPassParameters) { - .RenderTargets = - { - .Items = (ElemRenderPassRenderTarget[]) { - { - .RenderTarget = updateParameters->BackBufferRenderTarget, - .ClearColor = { 0.0f, 0.01f, 0.02f, 1.0f }, - }}, - .Length = 1 - } - }); - - ImGuidElementalNewFrame(updateParameters, inputStream); - igNewFrame(); - igShowDemoWindow(&applicationPayload->ShowImGuiDemoWindow); - igRender(); - - ImGuiRenderDrawData(igGetDrawData(), commandList, applicationPayload, updateParameters); - - ElemEndRenderPass(commandList); - - ElemCommitCommandList(commandList); - applicationPayload->LastExecutionFence = ElemExecuteCommandList(applicationPayload->CommandQueue, commandList, NULL); - - ElemPresentSwapChain(applicationPayload->SwapChain); - SampleFrameMeasurement frameMeasurement = SampleEndFrameMeasurement(); - - if (frameMeasurement.HasNewData) - { - SampleSetWindowTitle(applicationPayload->Window, "HelloImGui", applicationPayload->GraphicsDevice, frameMeasurement.FrameTimeInSeconds, frameMeasurement.Fps); - } - - SampleStartFrameMeasurement(); -} - -int main(int argc, const char* argv[]) -{ - bool preferVulkan = false; - - if (argc > 1 && strcmp(argv[1], "--vulkan") == 0) - { - preferVulkan = true; - } - - ElemConfigureLogHandler(ElemConsoleLogHandler); - - ApplicationPayload payload = - { - .PreferVulkan = preferVulkan - }; - - ElemRunApplication(&(ElemRunApplicationParameters) - { - .ApplicationName = "Hello ImGui", - .InitHandler = InitSample, - .FreeHandler = FreeSample, - .Payload = &payload - }); -} diff --git a/src/Elemental/Common/Graphics/UploadBufferPool.cpp b/src/Elemental/Common/Graphics/UploadBufferPool.cpp index 7cc7716a..f1a7b5ae 100644 --- a/src/Elemental/Common/Graphics/UploadBufferPool.cpp +++ b/src/Elemental/Common/Graphics/UploadBufferPool.cpp @@ -25,7 +25,7 @@ UploadBufferMemory GetUploadBufferPoolItem(UploadBufferDevicePool* uploadB uploadBufferPool->CurrentUploadBufferIndex = (uploadBufferPool->CurrentUploadBufferIndex + 1) % MAX_UPLOAD_BUFFERS; auto currentUploadBuffer = uploadBufferPool->CurrentUploadBuffer; - uint64_t uploadBufferSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512llu * 1024u * 1024u); + uint64_t uploadBufferSize = SystemMin(uploadBufferPool->LastBufferSize * 2u, 512u * 1024u * 1024u); if (uploadBufferSize == 0) { diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index 4d55b730..f39ba92d 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -1,12 +1,10 @@ #include "VulkanCommandList.h" +#include "VulkanConfig.h" #include "VulkanGraphicsDevice.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" -#define VULKAN_MAX_COMMANDQUEUES 10u -#define VULKAN_MAX_COMMANDLISTS 64u - SystemDataPool vulkanCommandQueuePool; SystemDataPool vulkanCommandListPool; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanConfig.h b/src/Elemental/Common/Graphics/Vulkan/VulkanConfig.h new file mode 100644 index 00000000..a8da5a0c --- /dev/null +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanConfig.h @@ -0,0 +1,21 @@ +#pragma once + +#define VULKAN_MEMORY_ARENA 512 * 1024 * 1024 +#define VULKAN_READBACK_MEMORY_ARENA 32 * 1024 * 1024 + +#define VULKAN_MAX_DEVICES 10u + +#define VULKAN_MAX_COMMANDQUEUES 10u +#define VULKAN_MAX_COMMANDLISTS 64u + +#define VULKAN_MAX_GRAPHICSHEAP 32 +#define VULKAN_MAX_RESOURCES 1000000 +#define VULKAN_MAX_SAMPLERS 2048 +#define VULKAN_MAX_MIPS 16 + +#define VULKAN_MAX_SWAPCHAIN_BUFFERS 3 +#define VULKAN_MAX_SWAPCHAINS 10u + +#define VULKAN_MAX_LIBRARIES UINT16_MAX +#define VULKAN_MAX_PIPELINESTATES UINT16_MAX + diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 2835c617..088b6708 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -1,4 +1,5 @@ #include "VulkanGraphicsDevice.h" +#include "VulkanConfig.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemLogging.h" @@ -725,7 +726,7 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(handle); graphicsDeviceData->ResourceDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, VULKAN_MAX_RESOURCES); - graphicsDeviceData->SamplerDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, VULKAN_MAX_SAMPLER); + graphicsDeviceData->SamplerDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, VULKAN_MAX_SAMPLERS); // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) graphicsDeviceData->UploadBufferPools = SystemPushArray*>(VulkanGraphicsMemoryArena, MAX_UPLOAD_BUFFERS); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h index fe358ea2..7b5c14e3 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.h @@ -12,13 +12,6 @@ #endif #include "volk.h" -#define VULKAN_MAX_DEVICES 10u - -// TODO: Investigate why we cannot bump this to 1 million -//#define VULKAN_MAX_RESOURCES 400000 -#define VULKAN_MAX_RESOURCES 1000000 -#define VULKAN_MAX_SAMPLER 2048 - struct VulkanDescriptorHeapStorage; struct VulkanDescriptorSet; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp index aeaeab30..d189427c 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResource.cpp @@ -1,24 +1,20 @@ #include "VulkanResource.h" +#include "VulkanConfig.h" #include "VulkanGraphicsDevice.h" #include "VulkanCommandList.h" -#include "VulkanResourceBarrier.h" #include "Graphics/Resource.h" -#include "Graphics/ResourceBarrier.h" #include "Graphics/ResourceDeleteQueue.h" #include "Graphics/UploadBufferPool.h" #include "SystemDataPool.h" #include "SystemFunctions.h" #include "SystemMemory.h" -#define VULKAN_MAX_GRAPHICSHEAP 32 -#define VULKAN_MAX_SAMPLERS 2048 - SystemDataPool vulkanGraphicsHeapPool; SystemDataPool vulkanGraphicsResourcePool; thread_local UploadBufferDevicePool threadVulkanUploadBufferPools[VULKAN_MAX_DEVICES]; -// TODO: This descriptor infos should be linked to the graphics device like the resource desc heaps +// TODO: IMPORTANT: This descriptor infos should be linked to the graphics device like the resource desc heaps Span vulkanResourceDescriptorInfos; Span vulkanSamplerInfos; // TODO: To refactor @@ -671,7 +667,7 @@ ElemDataSpan VulkanDownloadGraphicsBufferData(ElemGraphicsResource resource, con VulkanUploadBuffer CreateVulkanUploadBuffer(ElemGraphicsDevice graphicsDevice, uint64_t sizeInBytes) { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Create Vulkan UploadBuffer with : %d\n", sizeInBytes); + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Create Vulkan UploadBuffer with : %d", sizeInBytes); SystemAssert(graphicsDevice); auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); @@ -1111,7 +1107,7 @@ ElemGraphicsSampler VulkanCreateGraphicsSampler(ElemGraphicsDevice graphicsDevic samplerCreateInfo.compareEnable = localSamplerInfo.CompareFunction != ElemGraphicsCompareFunction_Never; samplerCreateInfo.compareOp = ConvertToVulkanCompareFunction(localSamplerInfo.CompareFunction); samplerCreateInfo.minLod = localSamplerInfo.MinLod; - samplerCreateInfo.maxLod = localSamplerInfo.MaxLod == 0 ? 1000 : localSamplerInfo.MaxLod; + samplerCreateInfo.maxLod = localSamplerInfo.MaxLod == 0 ? VULKAN_MAX_MIPS : localSamplerInfo.MaxLod; samplerCreateInfo.borderColor = borderColor; VkSampler sampler; @@ -1170,9 +1166,14 @@ void VulkanFreeGraphicsSampler(ElemGraphicsSampler sampler, const ElemFreeGraphi return; } - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(vulkanSamplerInfos[sampler].GraphicsDevice); - SystemAssert(graphicsDeviceData); + auto samplerInfo = vulkanSamplerInfos[sampler]; + + if (samplerInfo.VulkanSampler != VK_NULL_HANDLE) + { + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(samplerInfo.GraphicsDevice); + SystemAssert(graphicsDeviceData); - vkDestroySampler(graphicsDeviceData->Device, vulkanSamplerInfos[sampler].VulkanSampler, nullptr); - vulkanSamplerInfos[sampler] = {}; + vkDestroySampler(graphicsDeviceData->Device, samplerInfo.VulkanSampler, nullptr); + vulkanSamplerInfos[sampler] = {}; + } } diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp index 330f3c5d..a9d6e8b9 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanShader.cpp @@ -1,4 +1,5 @@ #include "VulkanShader.h" +#include "VulkanConfig.h" #include "VulkanGraphicsDevice.h" #include "VulkanCommandList.h" #include "VulkanResource.h" @@ -9,9 +10,6 @@ #include "SystemFunctions.h" #include "SystemMemory.h" -#define VULKAN_MAX_LIBRARIES UINT16_MAX -#define VULKAN_MAX_PIPELINESTATES UINT16_MAX - SystemDataPool vulkanShaderLibraryPool; SystemDataPool vulkanPipelineStatePool; diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 197739c2..c0145732 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -1,4 +1,5 @@ #include "VulkanSwapChain.h" +#include "VulkanConfig.h" #include "VulkanGraphicsDevice.h" #include "VulkanCommandList.h" #include "VulkanResource.h" @@ -15,8 +16,6 @@ #include "../Elemental/Linux/WaylandWindow.h" #endif -#define VULKAN_MAX_SWAPCHAINS 10u - SystemDataPool vulkanSwapChainPool; void InitVulkanSwapChainMemory() @@ -200,7 +199,7 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) // Looks like a windowed issue where we miss the vsync // The issue seems to be resolved for now... to monitor! #ifdef _WIN32 - auto presentId = swapChainData->PresentId - swapChainData->FrameLatency; + //auto presentId = swapChainData->PresentId - swapChainData->FrameLatency; //if (presentId < 10) //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "PresentID: %d", presentId); diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h index d7cf81e2..130a7da5 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h @@ -1,6 +1,7 @@ #pragma once #include "Elemental.h" +#include "VulkanConfig.h" #ifdef WIN32 #define VK_USE_PLATFORM_WIN32_KHR @@ -9,8 +10,6 @@ #endif #include "volk.h" -#define VULKAN_MAX_SWAPCHAIN_BUFFERS 3 - struct VulkanSwapChainData { VkSwapchainKHR DeviceObject; diff --git a/tests/GraphicsTests/Data/Assert.hlsl b/tests/GraphicsTests/Data/Assert.hlsl index 137ece4e..67ddadf3 100644 --- a/tests/GraphicsTests/Data/Assert.hlsl +++ b/tests/GraphicsTests/Data/Assert.hlsl @@ -20,7 +20,10 @@ void CopyTexture(uint2 threadId: SV_DispatchThreadID) float mipLevelCount; sourceTexture.GetDimensions(parameters.MipLevel, width, height, mipLevelCount); - destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); + if (threadId.x < width && threadId.y < height) + { + destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); + } } [shader("compute")] @@ -35,5 +38,8 @@ void CopyTextureFloat(uint2 threadId: SV_DispatchThreadID) float mipLevelCount; sourceTexture.GetDimensions(parameters.MipLevel, width, height, mipLevelCount); - destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); + if (threadId.x < width && threadId.y < height) + { + destinationBuffer[threadId.y * width + threadId.x] = sourceTexture.Load(uint3(threadId, parameters.MipLevel)); + } } diff --git a/tests/GraphicsTests/ResourceIOTests.cpp b/tests/GraphicsTests/ResourceIOTests.cpp index f94e41b0..16d6a702 100644 --- a/tests/GraphicsTests/ResourceIOTests.cpp +++ b/tests/GraphicsTests/ResourceIOTests.cpp @@ -490,6 +490,7 @@ UTEST(ResourceIO, CopyDataToGraphicsResource_WithTexture) ElemDataSpan outputMipData[mipLevelCount]; + // BUG: Problem in vulkan with last mip level for (uint32_t i = 0; i < mipLevelCount; i++) { auto currentWidth = Max(1u, width >> i); From 7d2aae105291be352d2b525bf352752cc374fe48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 18 Jan 2025 06:07:48 +0100 Subject: [PATCH 92/93] Update to vulkan 1.4 --- .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 16 ++++++++-------- src/Elemental/Elemental.h | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 088b6708..79187401 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -66,10 +66,10 @@ void InitVulkan() auto stackMemoryArena = SystemGetStackMemoryArena(); AssertIfFailed(volkInitialize()); - SystemAssert(volkGetInstanceVersion() >= VK_API_VERSION_1_3); + SystemAssert(volkGetInstanceVersion() >= VK_API_VERSION_1_4); VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - appInfo.apiVersion = VK_API_VERSION_1_3; + appInfo.apiVersion = VK_API_VERSION_1_4; VkInstanceCreateInfo createInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; createInfo.pApplicationInfo = &appInfo; @@ -358,7 +358,7 @@ bool VulkanCheckGraphicsDeviceCompatibility(VkPhysicalDevice device) vkGetPhysicalDeviceFeatures2(device, &features2); - if (meshShaderFeatures.meshShader && meshShaderFeatures.taskShader && presentIdFeatures.presentId) + if (meshShaderFeatures.meshShader && presentIdFeatures.presentId) { return true; } @@ -671,8 +671,8 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o features13.dynamicRendering = true; features13.shaderDemoteToHelperInvocation = true; - VkPhysicalDeviceMaintenance5Features maintenance5 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_FEATURES }; - maintenance5.maintenance5 = true; + VkPhysicalDeviceVulkan14Features features14 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES }; + features14.maintenance5 = true; VkPhysicalDeviceMeshShaderFeaturesEXT meshFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; meshFeatures.meshShader = true; @@ -690,11 +690,11 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o createInfo.pNext = &features; features.pNext = &features12; features12.pNext = &features13; - features13.pNext = &presentIdFeatures; + features13.pNext = &features14; + features14.pNext = &presentIdFeatures; presentIdFeatures.pNext = &presentWaitFeatures; presentWaitFeatures.pNext = &meshFeatures; - meshFeatures.pNext = &maintenance5; - maintenance5.pNext = &mutableDescriptorFeatures; + meshFeatures.pNext = &mutableDescriptorFeatures; VkDevice device = nullptr; AssertIfFailedReturnNullHandle(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); diff --git a/src/Elemental/Elemental.h b/src/Elemental/Elemental.h index dfabc7e6..34fdbbbb 100644 --- a/src/Elemental/Elemental.h +++ b/src/Elemental/Elemental.h @@ -382,7 +382,9 @@ typedef enum typedef enum { ElemGraphicsHeapType_Gpu = 0, - ElemGraphicsHeapType_GpuUpload = 1, // TODO: Still not convinced about that one. should we let GPU be gpu upload by default? + // TODO: Still not convinced about that one. should we let GPU be gpu upload by default? + // TODO: This may cause a bug in vulkan with textures (flickering) + ElemGraphicsHeapType_GpuUpload = 1, ElemGraphicsHeapType_Readback = 2 } ElemGraphicsHeapType; From c8a3956930dbcb776fea1f50292f2f057ec278d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Sat, 18 Jan 2025 06:12:00 +0100 Subject: [PATCH 93/93] Fix shader compiler --- src/ElementalTools/Shaders/DirectXShaderCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp index 73d53b71..1e8e3af7 100644 --- a/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp +++ b/src/ElementalTools/Shaders/DirectXShaderCompiler.cpp @@ -10,7 +10,7 @@ void InitDirectXShaderCompiler() { if (!directXShaderCompilerLibrary.Handle) { - directXShaderCompilerLibrary = SystemLoadLibrary("./dxcompiler"); + directXShaderCompilerLibrary = SystemLoadLibrary("dxcompiler"); if (directXShaderCompilerLibrary.Handle != nullptr) {