#include "GPU.h" #include #include #include #include #include SDL_GPUDevice* GPU::device = nullptr; SDL_GPUTexture* GPU::swapchain = nullptr; SDL_Window* GPU::window = nullptr; std::stack GPU::commandbuffers; SDL_GPURenderPass* GPU::renderPass = nullptr; SDL_GPUCopyPass* GPU::copyPass = nullptr; std::string GPU::GetDetails() { SDL_PropertiesID deviceProps = SDL_GetGPUDeviceProperties(device); const char* name = SDL_GetStringProperty(deviceProps, SDL_PROP_GPU_DEVICE_NAME_STRING, "Unknown Device"); const char* driver = SDL_GetGPUDeviceDriver(device); bool debugMode = SDL_GetBooleanProperty(deviceProps, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, false); std::string details = std::string(name) + " (" + std::string(driver) + (debugMode ? " with validation layers" : "") + ")"; return details; } bool GPU::Init() { const char* debugEnv = std::getenv("GRADIEN_GPU_DEBUG"); bool debugMode = (debugEnv != nullptr && std::string(debugEnv) == "1"); SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_DEBUGMODE_BOOLEAN, debugMode); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_SHADERS_SPIRV_BOOLEAN, true); // maximize compatibility SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_CLIP_DISTANCE_BOOLEAN, false); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_DEPTH_CLAMPING_BOOLEAN, false); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_INDIRECT_DRAW_FIRST_INSTANCE_BOOLEAN, false); SDL_SetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN, false); device = SDL_CreateGPUDeviceWithProperties(props); SDL_DestroyProperties(props); if (!device) { return false; } SDL_PropertiesID deviceProps = SDL_GetGPUDeviceProperties(device); const char* name = SDL_GetStringProperty(deviceProps, SDL_PROP_GPU_DEVICE_NAME_STRING, "Unknown Device"); std::printf(">>> %s\n", GetDetails().c_str()); // warn on nvidia if (strstr(name, "NVIDIA") != nullptr || strstr(name, "nvidia") != nullptr) { std::printf("Warning: Unsupported configuration. Stuttering may occur. Consider using a different GPU/driver if you encounter frame pacing issues. See: https://github.com/libsdl-org/SDL/issues/15069\n"); } return true; } void GPU::Shutdown() { SDL_ReleaseWindowFromGPUDevice(device, window); SDL_DestroyGPUDevice(device); device = nullptr; } SDL_GPUDevice* GPU::GetDevice() { return device; } SDL_Window* GPU::GetWindow() { return window; } SDL_GPUCommandBuffer* GPU::GetCommandBuffer() { return commandbuffers.top(); } SDL_GPURenderPass* GPU::GetRenderPass() { return renderPass; } SDL_GPUCopyPass* GPU::GetCopyPass() { return copyPass; } bool GPU::ClaimWindow(SDL_Window* window) { GPU::window = window; if (!SDL_ClaimWindowForGPUDevice(device, window)) { return false; } SDL_SetGPUSwapchainParameters(device, window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, SDL_GPU_PRESENTMODE_VSYNC); // TODO: option for mailbox/unlocked (unlocked mode requires better handling of scrollarea since too high and scrolling breaks) return true; } bool GPU::AquirePushCommandBuffer() { auto* commandbuffer = SDL_AcquireGPUCommandBuffer(device); if (commandbuffer) { GPU::commandbuffers.push(commandbuffer); } return (commandbuffer) != nullptr; } bool GPU::WaitAquireSwapchainWithCommandBuffer() { if (!(commandbuffers.size() > 0)) { if (!AquirePushCommandBuffer()) { return false; } } bool result = SDL_WaitAndAcquireGPUSwapchainTexture(commandbuffers.top(), window, &swapchain, nullptr, nullptr); if (!result) { return false; } if (!swapchain) { SDL_CancelGPUCommandBuffer(commandbuffers.top()); commandbuffers.pop(); return false; } return true; } SDL_GPUTexture* GPU::GetSwapchain() { return swapchain; } SDL_GPUTextureFormat GPU::GetSwapchainFormat() { return SDL_GetGPUSwapchainTextureFormat(device, window); } void GPU::BeginRendering(std::list colorTargetInfos, SDL_GPUDepthStencilTargetInfo* depthStencilTargetInfo) { //create array std::vector colorTargets; colorTargets.reserve(colorTargetInfos.size()); for (SDL_GPUColorTargetInfo* colorTargetInfo : colorTargetInfos) { colorTargets.push_back(*colorTargetInfo); } renderPass = SDL_BeginGPURenderPass(commandbuffers.top(), colorTargets.data(), colorTargets.size(), depthStencilTargetInfo); } void GPU::EndRendering() { SDL_EndGPURenderPass(renderPass); GPU::renderPass = nullptr; } void GPU::BeginCopying() { copyPass = SDL_BeginGPUCopyPass(commandbuffers.top()); } void GPU::EndCopying() { SDL_EndGPUCopyPass(copyPass); GPU::copyPass = nullptr; } void GPU::SubmitCommandsPopCommandBuffer() { SDL_SubmitGPUCommandBuffer(commandbuffers.top()); GPU::commandbuffers.pop(); } void GPU::SubmitCommandsPopCommandBufferAwaitFence() { SDL_GPUFence* fence = SDL_SubmitGPUCommandBufferAndAcquireFence(commandbuffers.top()); GPU::commandbuffers.pop(); SDL_WaitForGPUFences(device, true, &fence, 1); SDL_ReleaseGPUFence(device, fence); }