This commit is contained in:
Isuru Samarathunga
2025-09-13 00:16:07 +05:30
parent a0b5dc2af2
commit 8a2baf6659
20 changed files with 698 additions and 27 deletions

View File

@ -0,0 +1,115 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/Rendering/GPUBuffer.hpp>
#include <SDL3/SDL_gpu.h>
namespace ia::iae
{
EXTERN SDL_GPUDevice *g_gpuDevice;
UINT32 g_stagingBufferSize = 4096;
SDL_GPUTransferBuffer* g_stagingBuffer{};
RefPtr<GPUBuffer> GPUBuffer::Create(IN PCVOID data, IN UINT32 dataSize)
{
const auto res = MakeRefPtr<GPUBuffer>();
SDL_GPUBufferCreateInfo createInfo{
.usage = SDL_GPU_BUFFERUSAGE_VERTEX,
.size = dataSize
};
SDL_GPUBuffer* handle{};
if(!(handle = SDL_CreateGPUBuffer(g_gpuDevice, &createInfo)))
{
IAE_LOG_ERROR("Couldn't create a SDL3 GPU Buffer: ", SDL_GetError());
return nullptr;
}
if(data && dataSize)
{
if(!EnsureStagingBufferSize((UINT32)dataSize))
return nullptr;
const auto mappedPtr = SDL_MapGPUTransferBuffer(g_gpuDevice, g_stagingBuffer, false);
memcpy(mappedPtr, data, dataSize);
SDL_UnmapGPUTransferBuffer(g_gpuDevice, g_stagingBuffer);
const auto cmdBuffer = SDL_AcquireGPUCommandBuffer(g_gpuDevice);
const auto copyPass = SDL_BeginGPUCopyPass(cmdBuffer);
SDL_GPUTransferBufferLocation src{
.transfer_buffer = g_stagingBuffer,
.offset = 0
};
SDL_GPUBufferRegion dst{
.buffer = handle,
.offset = 0,
.size = dataSize
};
SDL_UploadToGPUBuffer(copyPass, &src, &dst, false);
SDL_EndGPUCopyPass(copyPass);
SDL_SubmitGPUCommandBuffer(cmdBuffer);
}
res->m_handle = (Handle)handle;
return res;
}
BOOL GPUBuffer::InitializeStagingBuffer()
{
SDL_GPUTransferBufferCreateInfo createInfo{
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = g_stagingBufferSize
};
if(!(g_stagingBuffer = SDL_CreateGPUTransferBuffer(g_gpuDevice, &createInfo)))
{
IAE_LOG_ERROR("Couldn't create a SDL3 GPU Staging Buffer: ", SDL_GetError());
return false;
}
return true;
}
VOID GPUBuffer::TerminateStagingBuffer()
{
if(!g_stagingBuffer)
return;
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, g_stagingBuffer);
}
BOOL GPUBuffer::EnsureStagingBufferSize(IN UINT32 size)
{
if(!g_stagingBuffer)
return false;
if(size <= g_stagingBufferSize)
return true;
SDL_ReleaseGPUTransferBuffer(g_gpuDevice, g_stagingBuffer);
SDL_GPUTransferBufferCreateInfo createInfo{
.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,
.size = (g_stagingBufferSize = size)
};
if(!(g_stagingBuffer = SDL_CreateGPUTransferBuffer(g_gpuDevice, &createInfo)))
{
IAE_LOG_ERROR("Couldn't create a SDL3 GPU Staging Buffer: ", SDL_GetError());
return false;
}
return true;
}
} // namespace ia::iae

View File

@ -0,0 +1,44 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/Rendering/GPUBuffer.hpp>
#include <IAEngine/Rendering/Mesh/Quad.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
#include <IAEngine/Rendering/Types.hpp>
namespace ia::iae
{
RefPtr<GPUBuffer> g_quadMeshVertexBuffer{};
VOID QuadMesh::Initialize()
{
Vertex_Mesh vertices[6] = {{iam::Vec3f{-1, 1, 0}, iam::Vec2f{0, 0}}, {iam::Vec3f{1, 1, 0}, iam::Vec2f{1, 0}},
{iam::Vec3f{1, -1, 0}, iam::Vec2f{1, 1}}, {iam::Vec3f{-1, 1, 0}, iam::Vec2f{0, 0}},
{iam::Vec3f{1, -1, 0}, iam::Vec2f{1, 1}}, {iam::Vec3f{-1, -1, 0}, iam::Vec2f{0, 1}}};
g_quadMeshVertexBuffer = GPUBuffer::Create(&vertices, sizeof(vertices));
}
VOID QuadMesh::Terminate()
{
if(g_quadMeshVertexBuffer)
g_quadMeshVertexBuffer.reset();
}
VOID QuadMesh::Draw(IN CONST iam::Vec3f &position, IN CONST iam::Vec3f &scale, IN FLOAT32 rotation)
{
Renderer::Draw((Handle)g_quadMeshVertexBuffer.get(), 6);
}
} // namespace ia::iae

View File

@ -0,0 +1,83 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/Rendering/Pipeline/Pipeline.hpp>
#include <IACore/File.hpp>
#include <SDL3/SDL_gpu.h>
namespace ia::iae
{
EXTERN SDL_GPUDevice *g_gpuDevice;
Handle IPipeline::LoadShaderFromMemory(IN ShaderStage _stage, IN PCUINT8 sourceData, IN SIZE_T sourceLength,
IN UINT32 samplerCount, IN UINT32 uniformBufferCount,
IN UINT32 storageBufferCount, IN UINT32 storageTextureCount)
{
SDL_GPUShader* res{};
SDL_GPUShaderStage stage{};
switch (_stage)
{
case ShaderStage::VERTEX:
stage = SDL_GPU_SHADERSTAGE_VERTEX;
break;
case ShaderStage::PIXEL:
stage = SDL_GPU_SHADERSTAGE_FRAGMENT;
break;
default:
THROW_INVALID_DATA("Unsupported shader stage");
break;
}
SDL_GPUShaderCreateInfo createInfo = {
.code_size = sourceLength,
.code = sourceData,
.entrypoint = "main",
.format = SDL_GPU_SHADERFORMAT_SPIRV,
.stage = stage,
.num_samplers = samplerCount,
.num_storage_textures = storageTextureCount,
.num_storage_buffers = storageBufferCount,
.num_uniform_buffers = uniformBufferCount,
};
if(!(res = SDL_CreateGPUShader(g_gpuDevice, &createInfo)))
{
IAE_LOG_ERROR("Failed to create a SDL shader: ", SDL_GetError());
return INVALID_HANDLE;
}
return (Handle)res;
}
Handle IPipeline::LoadShaderFromFile(IN ShaderStage stage, IN PCCHAR fileName, IN UINT32 samplerCount,
IN UINT32 uniformBufferCount, IN UINT32 storageBufferCount,
IN UINT32 storageTextureCount)
{
const auto source = File::ReadToVector(fileName);
return LoadShaderFromMemory(stage, source.data(), source.size(), samplerCount, uniformBufferCount,
storageBufferCount, storageTextureCount);
}
VOID IPipeline::UnloadShader(IN Handle handle)
{
SDL_ReleaseGPUShader(g_gpuDevice, (SDL_GPUShader *) handle);
}
} // namespace ia::iae

View File

@ -0,0 +1,89 @@
// IAEngine: 2D Game Engine by IA
// Copyright (C) 2025 IAS (ias@iasoft.dev)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/Rendering/Pipeline/UnlitMesh.hpp>
#include <SDL3/SDL_gpu.h>
#include <EmbeddedShaders.hpp>
namespace ia::iae
{
EXTERN SDL_GPUDevice *g_gpuDevice;
EXTERN SDL_Window *g_windowHandle;
Pipeline_UnlitMesh::~Pipeline_UnlitMesh()
{
}
RefPtr<Pipeline_UnlitMesh> Pipeline_UnlitMesh::Create()
{
const auto res = MakeRefPtr<Pipeline_UnlitMesh>();
const auto vertexShader = LoadShaderFromMemory(ShaderStage::VERTEX, SHADER_SOURCE_UNLITMESH_VERT, sizeof(SHADER_SOURCE_UNLITMESH_VERT), 0, 0, 0, 0);
const auto pixelShader = LoadShaderFromMemory(ShaderStage::PIXEL, SHADER_SOURCE_UNLITMESH_FRAG, sizeof(SHADER_SOURCE_UNLITMESH_FRAG), 0, 0, 0, 0);
SDL_GPUColorTargetDescription colorTargets[] = {
{.format = SDL_GetGPUSwapchainTextureFormat(g_gpuDevice, g_windowHandle)}};
SDL_GPUVertexBufferDescription vertexBuffers[] = {{
.slot = 0,
.pitch = sizeof(Vertex_Mesh),
.input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,
.instance_step_rate = 0,
}};
SDL_GPUVertexAttribute vertexAttributes[] = {
{.location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3, .offset = 0},
{.location = 1,
.buffer_slot = 0,
.format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,
.offset = sizeof(float) * 3}};
SDL_GPUGraphicsPipelineCreateInfo createInfo = {
.vertex_shader = (SDL_GPUShader *) vertexShader,
.fragment_shader = (SDL_GPUShader *) pixelShader,
.vertex_input_state =
SDL_GPUVertexInputState{.vertex_buffer_descriptions = vertexBuffers,
.num_vertex_buffers = sizeof(vertexBuffers) / sizeof(vertexBuffers[0]),
.vertex_attributes = vertexAttributes,
.num_vertex_attributes =
sizeof(vertexAttributes) / sizeof(vertexAttributes[0])},
.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,
.target_info =
{
.color_target_descriptions = colorTargets,
.num_color_targets = sizeof(colorTargets) / sizeof(colorTargets[0]),
},
};
SDL_GPUGraphicsPipeline* handle{};
if(!(handle = SDL_CreateGPUGraphicsPipeline(g_gpuDevice, &createInfo)))
{
IAE_LOG_ERROR("Failed to create a SDL graphics pipeline: ", SDL_GetError());
return nullptr;
}
UnloadShader(pixelShader);
UnloadShader(vertexShader);
res->m_handle = (Handle)handle;
return res;
}
VOID Pipeline_UnlitMesh::Bind(IN Handle renderPassHandle)
{
SDL_BindGPUGraphicsPipeline((SDL_GPURenderPass*)renderPassHandle, (SDL_GPUGraphicsPipeline*)m_handle);
}
} // namespace ia::iae

View File

@ -15,6 +15,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <IAEngine/IAEngine.hpp>
#include <IAEngine/Rendering/GPUBuffer.hpp>
#include <IAEngine/Rendering/Mesh/Quad.hpp>
#include <IAEngine/Rendering/Pipeline/UnlitMesh.hpp>
#include <IAEngine/Rendering/Renderer.hpp>
#include <SDL3/SDL_gpu.h>
@ -38,15 +41,15 @@ namespace ia::iae
// ImGUI State
ImGuiIO g_imGUIIO{};
ImDrawData* g_imDrawData{};
ImDrawData *g_imDrawData{};
RefPtr<Pipeline_UnlitMesh> g_pipelineUnlitMesh;
BOOL Renderer::Initialize(IN Engine *engine)
{
g_windowHandle = (SDL_Window *) engine->GetWindowHandle();
if (!(g_gpuDevice =
SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_DXIL | SDL_GPU_SHADERFORMAT_MSL,
engine->IsDebugMode, nullptr)))
if (!(g_gpuDevice = SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV, engine->IsDebugMode, nullptr)))
{
IAE_LOG_ERROR("Couldn't create SDL3 GPU Device: ", SDL_GetError());
return false;
@ -57,7 +60,8 @@ namespace ia::iae
IAE_LOG_ERROR("Couldn't initialize SDL3 GPU for the window: ", SDL_GetError());
return false;
}
SDL_SetGPUSwapchainParameters(g_gpuDevice, g_windowHandle, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC);
SDL_SetGPUSwapchainParameters(g_gpuDevice, g_windowHandle, SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
SDL_GPU_PRESENTMODE_VSYNC);
const auto mainScale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
@ -73,14 +77,20 @@ namespace ia::iae
style.ScaleAllSizes(mainScale);
style.FontScaleDpi = mainScale;
ImGui_ImplSDLGPU3_InitInfo initInfo{
.Device = g_gpuDevice,
.ColorTargetFormat = SDL_GetGPUSwapchainTextureFormat(g_gpuDevice, g_windowHandle),
.PresentMode = SDL_GPU_PRESENTMODE_VSYNC
};
ImGui_ImplSDLGPU3_InitInfo initInfo{.Device = g_gpuDevice,
.ColorTargetFormat =
SDL_GetGPUSwapchainTextureFormat(g_gpuDevice, g_windowHandle),
.PresentMode = SDL_GPU_PRESENTMODE_VSYNC};
ImGui_ImplSDL3_InitForSDLGPU(g_windowHandle);
ImGui_ImplSDLGPU3_Init(&initInfo);
if (!GPUBuffer::InitializeStagingBuffer())
return false;
g_pipelineUnlitMesh = Pipeline_UnlitMesh::Create();
QuadMesh::Initialize();
return true;
}
@ -88,6 +98,10 @@ namespace ia::iae
{
SDL_WaitForGPUIdle(g_gpuDevice);
QuadMesh::Terminate();
GPUBuffer::TerminateStagingBuffer();
ImGui_ImplSDL3_Shutdown();
ImGui_ImplSDLGPU3_Shutdown();
ImGui::DestroyContext();
@ -144,6 +158,8 @@ namespace ia::iae
colorTargetInfo.store_op = SDL_GPU_STOREOP_STORE;
g_renderPass = SDL_BeginGPURenderPass(g_cmdBuffer, &colorTargetInfo, 1, NULL);
g_pipelineUnlitMesh->Bind((Handle) g_renderPass);
}
VOID Renderer::EndFrame()
@ -155,4 +171,20 @@ namespace ia::iae
SDL_EndGPURenderPass(g_renderPass);
SDL_SubmitGPUCommandBuffer(g_cmdBuffer);
}
VOID Renderer::Draw(IN Handle vertexBufferHandle, IN INT32 vertexCount)
{
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0}};
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
SDL_DrawGPUPrimitives(g_renderPass, vertexCount, 1, 0, 0);
}
VOID Renderer::Draw(IN Handle vertexBufferHandle, IN Handle indexBufferHandle, IN INT32 indexCount)
{
SDL_GPUBufferBinding bindings[] = {{.buffer = (SDL_GPUBuffer *) vertexBufferHandle, .offset = 0},
{.buffer = (SDL_GPUBuffer *) indexBufferHandle, .offset = 0}};
SDL_BindGPUVertexBuffers(g_renderPass, 0, bindings, 1);
SDL_BindGPUIndexBuffer(g_renderPass, &bindings[1], SDL_GPU_INDEXELEMENTSIZE_32BIT);
SDL_DrawGPUIndexedPrimitives(g_renderPass, indexCount, 1, 0, 0, 0);
}
} // namespace ia::iae

View File

@ -23,7 +23,7 @@
namespace ia::iae
{
Texture::Texture(IN Engine *engine) : m_engine(engine)
Texture::Texture()
{
}
@ -35,8 +35,7 @@ namespace ia::iae
VOID Texture::Draw(IN CONST iam::Vec3f &position, IN CONST iam::Vec3f &scale, IN FLOAT32 rotation, IN BOOL flipH,
IN BOOL flipV, IN CONST iam::Vec4f &colorOverlay) CONST
{
SDL_FRect rect{.x = position.X, .y = position.Y, .w = m_width * scale.X, .h = m_height * scale.Y};
// SDL_FRect rect{.x = position.X, .y = position.Y, .w = m_width * scale.X, .h = m_height * scale.Y};
//SDL_SetTextureColorModFloat(TEXTURE_HANDLE, colorOverlay.X, colorOverlay.Y, colorOverlay.Z);
//SDL_SetTextureAlphaModFloat(TEXTURE_HANDLE, colorOverlay.W);
//SDL_RenderTextureRotated((SDL_Renderer *) m_engine->GetRendererHandle(), TEXTURE_HANDLE, nullptr, &rect,