mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-24 21:55:09 +00:00
302 lines
12 KiB
C++
302 lines
12 KiB
C++
#include "vulkan_graphics_backend.hpp"
|
|
|
|
#include "../../core/vertex.hpp"
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_vulkan.h>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
|
|
namespace sdl3cpp::services::impl {
|
|
|
|
VulkanGraphicsBackend::VulkanGraphicsBackend(std::shared_ptr<IVulkanDeviceService> deviceService,
|
|
std::shared_ptr<ISwapchainService> swapchainService,
|
|
std::shared_ptr<IRenderCommandService> renderCommandService,
|
|
std::shared_ptr<IPipelineService> pipelineService,
|
|
std::shared_ptr<IBufferService> bufferService,
|
|
std::shared_ptr<ILogger> logger)
|
|
: deviceService_(std::move(deviceService)),
|
|
swapchainService_(std::move(swapchainService)),
|
|
renderCommandService_(std::move(renderCommandService)),
|
|
pipelineService_(std::move(pipelineService)),
|
|
bufferService_(std::move(bufferService)),
|
|
logger_(std::move(logger)),
|
|
window_(nullptr),
|
|
initialized_(false),
|
|
currentImageIndex_(0),
|
|
frameCommands_(),
|
|
currentViewProj_{} {
|
|
logger_->Trace("VulkanGraphicsBackend", "VulkanGraphicsBackend",
|
|
"deviceService=" + std::string(deviceService_ ? "set" : "null") +
|
|
", swapchainService=" + std::string(swapchainService_ ? "set" : "null") +
|
|
", renderCommandService=" + std::string(renderCommandService_ ? "set" : "null") +
|
|
", pipelineService=" + std::string(pipelineService_ ? "set" : "null") +
|
|
", bufferService=" + std::string(bufferService_ ? "set" : "null"));
|
|
}
|
|
|
|
VulkanGraphicsBackend::~VulkanGraphicsBackend() {
|
|
logger_->Trace("VulkanGraphicsBackend", "~VulkanGraphicsBackend");
|
|
if (initialized_) {
|
|
Shutdown();
|
|
}
|
|
}
|
|
|
|
void VulkanGraphicsBackend::Initialize(void* window, const GraphicsConfig& config) {
|
|
logger_->Trace("VulkanGraphicsBackend", "Initialize");
|
|
|
|
if (initialized_) return;
|
|
|
|
window_ = static_cast<SDL_Window*>(window);
|
|
|
|
// Initialize Vulkan device
|
|
std::vector<const char*> deviceExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
|
deviceService_->Initialize(deviceExtensions, config.enableValidationLayers);
|
|
deviceService_->CreateSurface(window_);
|
|
deviceService_->CreateLogicalDevice();
|
|
|
|
// Get window size for swapchain
|
|
int width, height;
|
|
SDL_GetWindowSizeInPixels(window_, &width, &height);
|
|
|
|
// Initialize swapchain
|
|
swapchainService_->CreateSwapchain(static_cast<uint32_t>(width), static_cast<uint32_t>(height));
|
|
|
|
// Initialize render command service (this creates command pools/buffers)
|
|
// Note: RenderCommandService initialization happens in its constructor
|
|
|
|
initialized_ = true;
|
|
}
|
|
|
|
void VulkanGraphicsBackend::Shutdown() {
|
|
logger_->Trace("VulkanGraphicsBackend", "Shutdown");
|
|
|
|
if (!initialized_) return;
|
|
|
|
// Cleanup in reverse order
|
|
renderCommandService_->Cleanup();
|
|
pipelineService_->Cleanup();
|
|
bufferService_->Cleanup();
|
|
swapchainService_->CleanupSwapchain();
|
|
deviceService_->Shutdown();
|
|
|
|
initialized_ = false;
|
|
}
|
|
|
|
void VulkanGraphicsBackend::RecreateSwapchain(uint32_t width, uint32_t height) {
|
|
logger_->Trace("VulkanGraphicsBackend", "RecreateSwapchain",
|
|
"width=" + std::to_string(width) +
|
|
", height=" + std::to_string(height));
|
|
|
|
if (!initialized_) {
|
|
return;
|
|
}
|
|
|
|
if (width == 0 || height == 0) {
|
|
logger_->Warn("VulkanGraphicsBackend::RecreateSwapchain: Skipping swapchain recreation for zero size");
|
|
return;
|
|
}
|
|
|
|
deviceService_->WaitIdle();
|
|
swapchainService_->RecreateSwapchain(width, height);
|
|
|
|
VkExtent2D extent = swapchainService_->GetSwapchainExtent();
|
|
pipelineService_->RecreatePipelines(swapchainService_->GetRenderPass(), extent);
|
|
renderCommandService_->OnSwapchainRecreated();
|
|
}
|
|
|
|
void VulkanGraphicsBackend::WaitIdle() {
|
|
logger_->Trace("VulkanGraphicsBackend", "WaitIdle");
|
|
deviceService_->WaitIdle();
|
|
}
|
|
|
|
GraphicsDeviceHandle VulkanGraphicsBackend::CreateDevice() {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreateDevice");
|
|
// Device is already created in Initialize, just return a handle
|
|
return reinterpret_cast<GraphicsDeviceHandle>(deviceService_->GetDevice());
|
|
}
|
|
|
|
void VulkanGraphicsBackend::DestroyDevice(GraphicsDeviceHandle device) {
|
|
logger_->Trace("VulkanGraphicsBackend", "DestroyDevice");
|
|
// Device cleanup happens in Shutdown
|
|
}
|
|
|
|
GraphicsPipelineHandle VulkanGraphicsBackend::CreatePipeline(GraphicsDeviceHandle device, const std::string& shaderKey, const ShaderPaths& shaderPaths) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreatePipeline", "shaderKey=" + shaderKey);
|
|
|
|
// Register shader with pipeline service
|
|
pipelineService_->RegisterShader(shaderKey, shaderPaths);
|
|
|
|
// Compile pipeline with render pass from swapchain service
|
|
// Note: This assumes swapchain service has created the render pass
|
|
VkExtent2D extent = swapchainService_->GetSwapchainExtent();
|
|
if (logger_) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreatePipeline",
|
|
"swapchainExtent=" + std::to_string(extent.width) + "x" +
|
|
std::to_string(extent.height));
|
|
}
|
|
|
|
pipelineService_->CompileAll(swapchainService_->GetRenderPass(), extent);
|
|
|
|
// Return the pipeline handle
|
|
VkPipeline pipeline = pipelineService_->GetPipeline(shaderKey);
|
|
GraphicsPipelineHandle handle = reinterpret_cast<GraphicsPipelineHandle>(pipeline);
|
|
|
|
// Store the mapping from handle to key
|
|
pipelineToShaderKey_[handle] = shaderKey;
|
|
|
|
return handle;
|
|
}
|
|
|
|
void VulkanGraphicsBackend::DestroyPipeline(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline) {
|
|
logger_->Trace("VulkanGraphicsBackend", "DestroyPipeline");
|
|
// Pipeline cleanup happens in pipeline service Shutdown
|
|
}
|
|
|
|
GraphicsBufferHandle VulkanGraphicsBackend::CreateVertexBuffer(GraphicsDeviceHandle device, const std::vector<uint8_t>& data) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreateVertexBuffer", "data.size=" + std::to_string(data.size()));
|
|
|
|
if (data.empty()) {
|
|
logger_->Error("VulkanGraphicsBackend::CreateVertexBuffer: No vertex data provided");
|
|
return nullptr;
|
|
}
|
|
|
|
if (data.size() % sizeof(core::Vertex) != 0) {
|
|
logger_->Error("VulkanGraphicsBackend::CreateVertexBuffer: Vertex data size is not aligned to Vertex");
|
|
return nullptr;
|
|
}
|
|
|
|
const size_t vertexCount = data.size() / sizeof(core::Vertex);
|
|
if (logger_) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreateVertexBuffer",
|
|
"vertexCount=" + std::to_string(vertexCount));
|
|
}
|
|
|
|
std::vector<core::Vertex> vertices(vertexCount);
|
|
std::memcpy(vertices.data(), data.data(), data.size());
|
|
bufferService_->UploadVertexData(vertices);
|
|
|
|
return reinterpret_cast<GraphicsBufferHandle>(bufferService_->GetVertexBuffer());
|
|
}
|
|
|
|
GraphicsBufferHandle VulkanGraphicsBackend::CreateIndexBuffer(GraphicsDeviceHandle device, const std::vector<uint8_t>& data) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreateIndexBuffer", "data.size=" + std::to_string(data.size()));
|
|
|
|
if (data.empty()) {
|
|
logger_->Error("VulkanGraphicsBackend::CreateIndexBuffer: No index data provided");
|
|
return nullptr;
|
|
}
|
|
|
|
if (data.size() % sizeof(uint16_t) != 0) {
|
|
logger_->Error("VulkanGraphicsBackend::CreateIndexBuffer: Index data size is not aligned to uint16_t");
|
|
return nullptr;
|
|
}
|
|
|
|
const size_t indexCount = data.size() / sizeof(uint16_t);
|
|
if (logger_) {
|
|
logger_->Trace("VulkanGraphicsBackend", "CreateIndexBuffer",
|
|
"indexCount=" + std::to_string(indexCount));
|
|
}
|
|
|
|
std::vector<uint16_t> indices(indexCount);
|
|
std::memcpy(indices.data(), data.data(), data.size());
|
|
bufferService_->UploadIndexData(indices);
|
|
|
|
return reinterpret_cast<GraphicsBufferHandle>(bufferService_->GetIndexBuffer());
|
|
}
|
|
|
|
void VulkanGraphicsBackend::DestroyBuffer(GraphicsDeviceHandle device, GraphicsBufferHandle buffer) {
|
|
logger_->Trace("VulkanGraphicsBackend", "DestroyBuffer");
|
|
// Buffer cleanup happens in buffer service Shutdown
|
|
}
|
|
|
|
bool VulkanGraphicsBackend::BeginFrame(GraphicsDeviceHandle device) {
|
|
logger_->Trace("VulkanGraphicsBackend", "BeginFrame");
|
|
|
|
frameCommands_.clear();
|
|
return renderCommandService_->BeginFrame(currentImageIndex_);
|
|
}
|
|
|
|
bool VulkanGraphicsBackend::EndFrame(GraphicsDeviceHandle device) {
|
|
logger_->Trace("VulkanGraphicsBackend", "EndFrame");
|
|
|
|
// Record all accumulated commands
|
|
if (renderGraphEnabled_) {
|
|
renderCommandService_->RecordRenderGraph(currentImageIndex_, renderGraphDefinition_,
|
|
frameCommands_, currentViewProj_);
|
|
} else {
|
|
renderCommandService_->RecordCommands(currentImageIndex_, frameCommands_, currentViewProj_);
|
|
}
|
|
|
|
// End the frame
|
|
return renderCommandService_->EndFrame(currentImageIndex_);
|
|
}
|
|
|
|
void VulkanGraphicsBackend::SetViewProjection(const std::array<float, 16>& viewProj) {
|
|
logger_->Trace("VulkanGraphicsBackend", "SetViewProjection");
|
|
currentViewProj_ = viewProj;
|
|
}
|
|
|
|
void VulkanGraphicsBackend::SetRenderGraphDefinition(const RenderGraphDefinition& definition) {
|
|
logger_->Trace("VulkanGraphicsBackend", "SetRenderGraphDefinition",
|
|
"resources=" + std::to_string(definition.resources.size()) +
|
|
", passes=" + std::to_string(definition.passes.size()));
|
|
renderGraphDefinition_ = definition;
|
|
renderGraphEnabled_ = !definition.passes.empty();
|
|
}
|
|
|
|
void VulkanGraphicsBackend::Draw(GraphicsDeviceHandle device, GraphicsPipelineHandle pipeline,
|
|
GraphicsBufferHandle vertexBuffer, GraphicsBufferHandle indexBuffer,
|
|
uint32_t indexOffset, uint32_t indexCount, int32_t vertexOffset,
|
|
const std::array<float, 16>& modelMatrix) {
|
|
logger_->Trace("VulkanGraphicsBackend", "Draw",
|
|
"indexOffset=" + std::to_string(indexOffset) +
|
|
", indexCount=" + std::to_string(indexCount) +
|
|
", vertexOffset=" + std::to_string(vertexOffset));
|
|
|
|
// Find the shader key for this pipeline
|
|
auto it = pipelineToShaderKey_.find(pipeline);
|
|
if (it == pipelineToShaderKey_.end()) {
|
|
logger_->Error("VulkanGraphicsBackend::Draw: Pipeline not found in mapping");
|
|
return;
|
|
}
|
|
|
|
// Create a render command
|
|
RenderCommand command;
|
|
command.indexOffset = indexOffset;
|
|
command.indexCount = indexCount;
|
|
command.vertexOffset = vertexOffset;
|
|
command.shaderKey = it->second;
|
|
command.modelMatrix = modelMatrix;
|
|
|
|
// Accumulate the command for later recording
|
|
frameCommands_.push_back(command);
|
|
}
|
|
|
|
GraphicsDeviceHandle VulkanGraphicsBackend::GetPhysicalDevice() const {
|
|
logger_->Trace("VulkanGraphicsBackend", "GetPhysicalDevice");
|
|
return reinterpret_cast<GraphicsDeviceHandle>(deviceService_->GetPhysicalDevice());
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> VulkanGraphicsBackend::GetSwapchainExtent() const {
|
|
logger_->Trace("VulkanGraphicsBackend", "GetSwapchainExtent");
|
|
VkExtent2D extent = swapchainService_->GetSwapchainExtent();
|
|
return {extent.width, extent.height};
|
|
}
|
|
|
|
uint32_t VulkanGraphicsBackend::GetSwapchainFormat() const {
|
|
logger_->Trace("VulkanGraphicsBackend", "GetSwapchainFormat");
|
|
return static_cast<uint32_t>(swapchainService_->GetSwapchainImageFormat());
|
|
}
|
|
|
|
void* VulkanGraphicsBackend::GetCurrentCommandBuffer() const {
|
|
logger_->Trace("VulkanGraphicsBackend", "GetCurrentCommandBuffer");
|
|
return reinterpret_cast<void*>(renderCommandService_->GetCurrentCommandBuffer());
|
|
}
|
|
|
|
void* VulkanGraphicsBackend::GetGraphicsQueue() const {
|
|
logger_->Trace("VulkanGraphicsBackend", "GetGraphicsQueue");
|
|
return reinterpret_cast<void*>(deviceService_->GetGraphicsQueue());
|
|
}
|
|
|
|
} // namespace sdl3cpp::services::impl
|