-
Notifications
You must be signed in to change notification settings - Fork 152
Open
Description
On macOS with the Metal renderer, memory leaks every frame.
In 3 minutes memory usage of a simple app reached 6 GB.
Minimal repro:
quad.metal:
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct VertexIn
{
float2 position [[attribute(0)]];
float2 uv [[attribute(1)]];
};
struct VertexOut
{
float4 position [[position]];
float2 uv;
};
vertex VertexOut VS(VertexIn inp [[stage_in]])
{
VertexOut outp;
outp.position = float4(inp.position, 0.0, 1.0);
outp.uv = inp.uv;
return outp;
}
fragment float4 PS(VertexOut inp [[stage_in]])
{
return float4(inp.uv, 1.0, 1.0);
}main.mm:
#include <stdio.h>
#include <LLGL/LLGL.h>
#include <LLGL/Platform/NativeHandle.h>
#include <LLGL/Utils/VertexFormat.h>
#include <LLGL/Utils/TypeNames.h>
#include <memory>
#define GLFW_EXPOSE_NATIVE_COCOA
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#include <LLGL/LLGL.h>
class CustomSurface : public LLGL::Surface {
public:
CustomSurface(GLFWwindow *window, const LLGL::Extent2D& size) : m_size(size), m_wnd(window) {}
~CustomSurface();
bool GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) override;
LLGL::Extent2D GetContentSize() const override { return m_size; };
bool AdaptForVideoMode(LLGL::Extent2D* resolution, bool* fullscreen) override;
void ResetPixelFormat() override {};
LLGL::Display* FindResidentDisplay() const override { return LLGL::Display::GetPrimary(); };
bool ProcessEvents();
private:
LLGL::Extent2D m_size;
GLFWwindow* m_wnd = nullptr;
};
CustomSurface::~CustomSurface() {
glfwDestroyWindow(m_wnd);
}
bool CustomSurface::GetNativeHandle(void* nativeHandle, std::size_t nativeHandleSize) {
auto handle = reinterpret_cast<LLGL::NativeHandle*>(nativeHandle);
handle->responder = glfwGetCocoaWindow(m_wnd);
return true;
}
bool CustomSurface::AdaptForVideoMode(LLGL::Extent2D *resolution, bool *fullscreen) {
m_size = *resolution;
glfwSetWindowSize(m_wnd, m_size.width, m_size.height);
return true;
}
bool CustomSurface::ProcessEvents() {
glfwPollEvents();
return !glfwWindowShouldClose(m_wnd);
}
int main(void) {
LLGL::Log::RegisterCallbackStd();
if (!glfwInit()) return -1;
GLFWwindow *window = glfwCreateWindow(1280, 720, "AAA", nullptr, nullptr);
LLGL::Report report;
auto context = LLGL::RenderSystem::Load("Metal", &report);
const LLGL::Display* display = LLGL::Display::GetPrimary();
const std::uint32_t resScale = (display != nullptr ? static_cast<std::uint32_t>(display->GetScale()) : 1u);
const auto resolution = LLGL::Extent2D(1280 * resScale, 720 * resScale);
LLGL::SwapChainDescriptor swapChainDesc;
swapChainDesc.resolution = resolution;
auto surface = std::make_shared<CustomSurface>(window, resolution);
auto swapChain = context->CreateSwapChain(swapChainDesc, surface);
swapChain->SetVsyncInterval(0);
auto commands = context->CreateCommandBuffer(LLGL::CommandBufferFlags::ImmediateSubmit);
const auto& info = context->GetRendererInfo();
float vertices[] = {
-0.5f, -0.5f, 0.0f, 0.0f,
0.5f, -0.5f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 1.0f,
0.5f, 0.5f, 1.0f, 1.0f,
};
LLGL::VertexFormat vertexFormat;
vertexFormat.AppendAttribute({"a_position", LLGL::Format::RG32Float});
vertexFormat.AppendAttribute({"a_uv", LLGL::Format::RG32Float});
vertexFormat.SetStride(sizeof(float) * 4);
LLGL::BufferDescriptor vertexBufferDesc;
vertexBufferDesc.size = sizeof(vertices);
vertexBufferDesc.bindFlags = LLGL::BindFlags::VertexBuffer;
vertexBufferDesc.vertexAttribs = vertexFormat.attributes;
LLGL::Buffer* vertexBuffer = context->CreateBuffer(vertexBufferDesc, vertices);
LLGL::ShaderDescriptor vertexShaderDesc, fragmentShaderDesc;
vertexShaderDesc = { LLGL::ShaderType::Vertex, "assets/shaders/quad.metal", "VS", "1.1" };
fragmentShaderDesc = { LLGL::ShaderType::Fragment, "assets/shaders/quad.metal", "PS", "1.1" };
vertexShaderDesc.vertex.inputAttribs = vertexFormat.attributes;
LLGL::Shader* vertexShader = context->CreateShader(vertexShaderDesc);
LLGL::Shader* fragmentShader = context->CreateShader(fragmentShaderDesc);
LLGL::PipelineLayoutDescriptor pipelineLayoutDesc;
LLGL::PipelineLayout* pipelineLayout = context->CreatePipelineLayout(pipelineLayoutDesc);
LLGL::GraphicsPipelineDescriptor pipelineDesc;
pipelineDesc.vertexShader = vertexShader;
pipelineDesc.fragmentShader = fragmentShader;
pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.indexFormat = LLGL::Format::R32UInt;
pipelineDesc.primitiveTopology = LLGL::PrimitiveTopology::TriangleStrip;
pipelineDesc.renderPass = swapChain->GetRenderPass();
LLGL::PipelineState* pipelineState = context->CreatePipelineState(pipelineDesc);
if (const LLGL::Report* report = pipelineState->GetReport()) {
if (report->HasErrors()) LLGL::Log::Errorf("%s", report->GetText());
return -1;
}
double prevTick = glfwGetTime();
while (surface->ProcessEvents()) {
double currentTick = glfwGetTime();
const double deltaTime = (currentTick - prevTick);
prevTick = currentTick;
commands->Begin();
{
commands->SetVertexBuffer(*vertexBuffer);
commands->BeginRenderPass(*swapChain);
{
commands->Clear(LLGL::ClearFlags::Color, LLGL::ClearValue(0.0f, 0.0f, 0.0f, 1.0f));
commands->SetViewport(swapChain->GetResolution());
commands->SetPipelineState(*pipelineState);
commands->Draw(4, 0);
}
commands->EndRenderPass();
}
commands->End();
swapChain->Present();
}
return 0;
}Reactions are currently unavailable