From 4976e3bdcde11900009b0109051e2d4938aa0741 Mon Sep 17 00:00:00 2001 From: Dynamitos Date: Fri, 28 Feb 2025 10:41:29 +0900 Subject: [PATCH] it works somewhat, but not really --- src/main.cpp | 1 + src/metal/Compute.metal | 44 +++++++++++++++++++++++ src/metal/MetalRenderer.mm | 71 +++++++++++++++++++++++++------------- 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d25162b..8d6d96d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,6 +45,7 @@ int main() ImGui::InputInt("Samples", (int*)&render.numSamples); if (ImGui::Button("Render")) { + std::cout << "Test" << std::endl; renderer->startRender(camera, render); } ImGui::Text("Render Stats"); diff --git a/src/metal/Compute.metal b/src/metal/Compute.metal index c2cf0f8..824c726 100644 --- a/src/metal/Compute.metal +++ b/src/metal/Compute.metal @@ -272,3 +272,47 @@ kernel void computeKernel( accumulator.write(result, threadId); image.write(pow(max(result * resolver, 0), float4(0.45f)), threadId); } + + +// Screen filling quad in normalized device coordinates. +constant float2 quadVertices[] = { + float2(-1, -1), + float2(-1, 1), + float2( 1, 1), + float2(-1, -1), + float2( 1, 1), + float2( 1, -1) +}; + +struct CopyVertexOut { + float4 position [[position]]; + float2 uv; +}; + +// Simple vertex shader that passes through NDC quad positions. +vertex CopyVertexOut copyVertex(unsigned short vid [[vertex_id]]) { + float2 position = quadVertices[vid]; + + CopyVertexOut out; + + out.position = float4(position, 0, 1); + out.uv = position * 0.5f + 0.5f; + + return out; +} + +// Simple fragment shader that copies a texture and applies a simple tonemapping function. +fragment float4 copyFragment(CopyVertexOut in [[stage_in]], + texture2d tex) +{ + constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); + + float3 color = tex.sample(sam, in.uv).xyz; + + // Apply a simple tonemapping function to reduce the dynamic range of the + // input image into a range which the screen can display. + color = color / (1.0f + color); + + return float4(color, 1.0f); +} + diff --git a/src/metal/MetalRenderer.mm b/src/metal/MetalRenderer.mm index 9ce1adc..5208e2d 100644 --- a/src/metal/MetalRenderer.mm +++ b/src/metal/MetalRenderer.mm @@ -14,6 +14,7 @@ id drawable; id device; id library; id queue; +id renderQueue; id function; id computePipeline; id accumulator; @@ -22,15 +23,14 @@ id resultTexture; MTLRenderPassDescriptor* renderPass; id renderEncoder; id renderCmd; +id pipelineState; +static void glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "Glfw Error %d: %s\n", error, description); +} MetalRenderer::MetalRenderer() { - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls - width = 1920; height = 1080; device = MTLCreateSystemDefaultDevice(); @@ -38,6 +38,7 @@ MetalRenderer::MetalRenderer() library = [device newDefaultLibrary]; queue = [device newCommandQueue]; + renderQueue = [device newCommandQueue]; scene = new MetalScene(device, queue); @@ -45,32 +46,39 @@ MetalRenderer::MetalRenderer() NSError* error; computePipeline = [device newComputePipelineStateWithFunction:function error:&error]; - - glfwInit(); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + glfwSetErrorCallback(glfw_error_callback); + glfwInit(); float xscale = 1, yscale = 1; glfwGetMonitorContentScale(glfwGetPrimaryMonitor(), &xscale, &yscale); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); handle = glfwCreateWindow(width / xscale, height / yscale, "RayTracer", nullptr, nullptr); - int w, h; - glfwGetFramebufferSize(handle, &w, &h); - framebufferWidth = width; - framebufferHeight = height; ImGui_ImplGlfw_InitForOpenGL(handle, false); - ImGui_ImplMetal_Init((__bridge id)device); - + ImGui_ImplMetal_Init(device); + + NSWindow* cocoaWindow = glfwGetCocoaWindow(handle); metalLayer = [CAMetalLayer layer]; metalLayer.device = device; metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; - metalLayer.drawableSize = CGSizeMake(w, h); - metalLayer.framebufferOnly = true; - NSWindow* cocoaWindow = glfwGetCocoaWindow(handle); [[cocoaWindow contentView] setLayer:metalLayer]; - [[cocoaWindow contentView] setWantsLayer:YES]; - [[cocoaWindow contentView] setNeedsLayout:YES]; - + [[cocoaWindow contentView] setWantsLayer:true]; renderPass = [[MTLRenderPassDescriptor alloc] init]; + + MTLRenderPipelineDescriptor *renderDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; + + renderDescriptor.vertexFunction = [library newFunctionWithName:@"copyVertex"]; + renderDescriptor.fragmentFunction = [library newFunctionWithName:@"copyFragment"]; + + renderDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm; + + pipelineState = [device newRenderPipelineStateWithDescriptor:renderDescriptor error:&error]; } MetalRenderer::~MetalRenderer() {} @@ -83,16 +91,28 @@ void MetalRenderer::generate() { scene->generate(); } void MetalRenderer::beginFrame() { - drawable = [metalLayer nextDrawable]; - renderCmd = [queue commandBuffer]; + glfwPollEvents(); + int w, h; + glfwGetFramebufferSize(handle, &w, &h); + framebufferWidth = width; + framebufferHeight = height; + metalLayer.drawableSize = CGSizeMake(framebufferWidth, framebufferHeight); +drawable = [metalLayer nextDrawable]; + renderCmd = [renderQueue commandBuffer]; renderPass.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0); renderPass.colorAttachments[0].texture = drawable.texture; renderPass.colorAttachments[0].loadAction = MTLLoadActionClear; renderPass.colorAttachments[0].storeAction = MTLStoreActionStore; - renderEncoder = [renderCmd renderCommandEncoderWithDescriptor:renderPass]; ImGui_ImplMetal_NewFrame(renderPass); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); + renderEncoder = [renderCmd renderCommandEncoderWithDescriptor:renderPass]; + [renderEncoder setRenderPipelineState:pipelineState]; + + [renderEncoder setFragmentTexture:resultTexture atIndex:0]; + + // Draw a quad which fills the screen. + [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; } void MetalRenderer::update() @@ -102,6 +122,9 @@ void MetalRenderer::update() [renderEncoder endEncoding]; [renderCmd presentDrawable:drawable]; [renderCmd commit]; + [renderCmd release]; + [renderEncoder release]; + [drawable release]; } void MetalRenderer::render(Camera camera, RenderParameter parameter) @@ -176,6 +199,6 @@ void MetalRenderer::render(Camera camera, RenderParameter parameter) [encoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threadsPerThreadgroup]; [encoder endEncoding]; [cmdBuffer commit]; - [cmdBuffer waitUntilCompleted]; + sampleTimes.push_back(0); } }