Renderer.cc
1 #include "Renderer.hh" 2 3 import std; 4 5 std::expected<std::unique_ptr<Renderer>, RendererError> 6 Renderer::init(MTL::Device* device) 7 { 8 auto renderer = std::unique_ptr<Renderer>(new Renderer()); 9 10 renderer->device = device->retain(); 11 renderer->command_queue = renderer->device->newCommandQueue(); 12 renderer->angle = 0.0f; 13 renderer->frame = 0; 14 renderer->current_time = 0.0f; 15 16 if (!renderer->command_queue) { 17 return std::unexpected(RendererError( 18 RendererError::Kind::DeviceError, 19 nullptr)); 20 } 21 22 return renderer->buildShaders() 23 .transform([r = std::move(renderer)]() mutable { 24 r->buildBuffers(); 25 r->buildDepthStencilStates(); 26 r->frame_semaphore = dispatch_semaphore_create(MAX_FRAMES_IN_FLIGHT); 27 return std::move(r); 28 }); 29 } 30 31 Renderer::~Renderer() 32 { 33 this->vertex_data_buffer->release(); 34 35 for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { 36 this->uniform_buffer[i]->release(); 37 } 38 39 this->shader_lib->release(); 40 this->depth_stencil_state->release(); 41 this->pipeline_state->release(); 42 this->command_queue->release(); 43 this->device->release(); 44 } 45 46 std::expected<void, RendererError> 47 Renderer::buildShaders() 48 { 49 NS::Error* error = nullptr; 50 51 MTL::Library* mtl_lib = this->device->newLibrary(nsStringUtf8(SHADER_SRC), nullptr, &error); 52 if (!mtl_lib) { 53 return std::unexpected(RendererError( 54 RendererError::Kind::ShaderCompilationFailed, 55 error)); 56 } 57 58 MTL::Function* vertex_fn = mtl_lib->newFunction(nsStringUtf8("vertexMain")); 59 MTL::Function* frag_fn = mtl_lib->newFunction(nsStringUtf8("fragmentMain")); 60 61 if (!vertex_fn || !frag_fn) { 62 return std::unexpected(RendererError( 63 RendererError::Kind::EntrypointNotFound, 64 error)); 65 } 66 67 MTL::RenderPipelineDescriptor* desc = MTL::RenderPipelineDescriptor::alloc()->init(); 68 desc->setVertexFunction(vertex_fn); 69 desc->setFragmentFunction(frag_fn); 70 desc->colorAttachments()->object(0)->setPixelFormat( 71 MTL::PixelFormat::PixelFormatBGRA8Unorm_sRGB); 72 desc->setDepthAttachmentPixelFormat(MTL::PixelFormat::PixelFormatDepth32Float); 73 74 this->pipeline_state = this->device->newRenderPipelineState(desc, &error); 75 if (!this->pipeline_state) { 76 return std::unexpected(RendererError( 77 RendererError::Kind::PipelineCreationFailed, 78 error)); 79 } 80 81 this->shader_lib = mtl_lib; 82 83 vertex_fn->release(); 84 frag_fn->release(); 85 desc->release(); 86 87 return {}; 88 } 89 90 void Renderer::buildBuffers() 91 { 92 const usize vertex_data_size = sizeof(this->vertices); 93 this->vertex_data_buffer = this->device->newBuffer( 94 vertex_data_size, 95 MTL::ResourceStorageModeManaged); 96 97 memcpy(this->vertex_data_buffer->contents(), this->vertices, vertex_data_size); 98 this->vertex_data_buffer->didModifyRange(NS::Range::Make(0, vertex_data_size)); 99 100 // Create uniform buffers for each frame 101 const usize uniform_buffer_size = sizeof(Uniforms); 102 for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { 103 this->uniform_buffer[i] = this->device->newBuffer( 104 uniform_buffer_size, 105 MTL::ResourceStorageModeManaged); 106 } 107 } 108 109 void Renderer::buildDepthStencilStates() 110 { 111 MTL::DepthStencilDescriptor* desc = MTL::DepthStencilDescriptor::alloc()->init(); 112 113 desc->setDepthCompareFunction(MTL::CompareFunction::CompareFunctionLess); 114 desc->setDepthWriteEnabled(true); 115 116 this->depth_stencil_state = this->device->newDepthStencilState(desc); 117 desc->release(); 118 } 119 120 void Renderer::draw(MTK::View* view) 121 { 122 NS::AutoreleasePool* pool = NS::AutoreleasePool::alloc()->init(); 123 124 frame = (frame + 1) % MAX_FRAMES_IN_FLIGHT; 125 126 // Update uniforms 127 Uniforms* uniforms = static_cast<Uniforms*>( 128 this->uniform_buffer[frame]->contents()); 129 130 current_time += 0.016f; 131 132 uniforms->time = current_time; 133 uniforms->resolution = { 134 static_cast<float>(view->drawableSize().width), 135 static_cast<float>(view->drawableSize().height) 136 }; 137 138 uniforms->camera_position = { 0.0f, 0.0f, -3.0f }; 139 uniforms->camera_target = { 0.0f, 0.0f, 0.0f }; 140 uniforms->camera_up = { 0.0f, 1.0f, 0.0f }; 141 142 this->uniform_buffer[frame]->didModifyRange(NS::Range::Make(0, sizeof(Uniforms))); 143 144 MTL::CommandBuffer* cmd_buffer = this->command_queue->commandBuffer(); 145 146 dispatch_semaphore_wait(this->frame_semaphore, DISPATCH_TIME_FOREVER); 147 148 Renderer* renderer = this; 149 cmd_buffer->addCompletedHandler(^void(MTL::CommandBuffer* buffer) { 150 dispatch_semaphore_signal(renderer->frame_semaphore); 151 }); 152 153 MTL::RenderPassDescriptor* rpd = view->currentRenderPassDescriptor(); 154 MTL::RenderCommandEncoder* encoder = cmd_buffer->renderCommandEncoder(rpd); 155 156 encoder->setRenderPipelineState(this->pipeline_state); 157 encoder->setDepthStencilState(this->depth_stencil_state); 158 159 encoder->setVertexBuffer(this->vertex_data_buffer, 0, 0); 160 encoder->setVertexBuffer(this->uniform_buffer[frame], 0, 1); 161 encoder->setFragmentBuffer(this->uniform_buffer[frame], 0, 0); 162 163 encoder->drawPrimitives( 164 MTL::PrimitiveType::PrimitiveTypeTriangle, 165 /* vertexStart */ NS::UInteger(0), // vertex_id starting point 166 /* vertexCount */ NS::UInteger(6)); // 6 Vertices 2 Triangles 167 168 encoder->endEncoding(); 169 cmd_buffer->presentDrawable(view->currentDrawable()); 170 cmd_buffer->commit(); 171 172 angle += 0.01f; 173 174 pool->release(); 175 } 176 177 RendererError::RendererError(Kind kind) 178 : kind(kind) 179 , details(std::nullopt) { }; 180 181 RendererError::RendererError(Kind kind, NS::Error* error) 182 : kind(kind) 183 , details(error->localizedDescription()->retain()) 184 { 185 } 186 187 RendererError::~RendererError() 188 { 189 if (details) 190 details.value()->release(); 191 } 192 193 constexpr std::string_view RendererError::baseMessage(Kind kind) 194 { 195 switch (kind) { 196 case Kind::ShaderCompilationFailed: 197 return "Shader compilation failed"; 198 case Kind::EntrypointNotFound: 199 return "Shader entrypoint not found"; 200 case Kind::PipelineCreationFailed: 201 return "Pipeline creation failed"; 202 case Kind::DeviceError: 203 return "Metal device error"; 204 } 205 } 206 207 std::string RendererError::message() const 208 { 209 std::string_view base_msg = baseMessage(this->kind); 210 211 return this->details 212 .transform([base_msg](auto* details) { 213 return std::format("{}: {}", 214 base_msg, 215 details->utf8String()); 216 }) 217 .value_or(std::string(base_msg)); 218 }