# Metal Shader Backend - Notas de Implementación ## Estado Actual ✅ **Completado:** - Shaders MSL (Metal Shading Language) portados desde GLSL - Estructura básica de `MetalShader` class - Inicialización de Metal device y command queue - Compilación de shaders en runtime - Creación de pipeline state - Buffers de vértices, índices y uniforms ❌ **Pendiente:** - **Render loop completo** (la parte más crítica) - Obtener textura Metal desde SDL_Texture - Gestión de drawables y presentation ## Diferencias GLSL vs MSL | Concepto | GLSL (OpenGL) | MSL (Metal) | |----------|---------------|-------------| | Entrada vertex | `layout(location = 0) in vec2` | `[[attribute(0)]]` | | Salida vertex | `out vec2` | Struct con `[[position]]` | | Uniforms | `uniform vec2` | `constant` struct en `[[buffer(N)]]` | | Sampling | `texture(sampler2D, vec2)` | `texture.sample(sampler, float2)` | | Entry point | `void main()` | `vertex/fragment function_name()` | | Vector types | `vec2, vec3, vec4` | `float2, float3, float4` | ## Pasos para Completar el Render Loop El método `MetalShader::render()` necesita: ```objc 1. Obtener drawable del CAMetalLayer: id drawable = [metal_layer_ nextDrawable]; 2. Crear command buffer: id command_buffer = [command_queue_ commandBuffer]; 3. Crear render pass descriptor: MTLRenderPassDescriptor* pass_descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; pass_descriptor.colorAttachments[0].texture = drawable.texture; pass_descriptor.colorAttachments[0].loadAction = MTLLoadActionClear; pass_descriptor.colorAttachments[0].storeAction = MTLStoreActionStore; pass_descriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1); 4. Crear render command encoder: id encoder = [command_buffer renderCommandEncoderWithDescriptor:pass_descriptor]; 5. Configurar pipeline y buffers: [encoder setRenderPipelineState:pipeline_state_]; [encoder setVertexBuffer:vertex_buffer_ offset:0 atIndex:0]; [encoder setVertexBuffer:uniforms_buffer_ offset:0 atIndex:1]; [encoder setFragmentBuffer:uniforms_buffer_ offset:0 atIndex:1]; [encoder setFragmentTexture:game_texture offset:0 atIndex:0]; [encoder setFragmentSamplerState:sampler_state_ atIndex:0]; 6. Dibujar: [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle indexCount:6 indexType:MTLIndexTypeUInt16 indexBuffer:index_buffer_ indexBufferOffset:0]; 7. Finalizar: [encoder endEncoding]; [command_buffer presentDrawable:drawable]; [command_buffer commit]; ``` ## Problema Crítico: Obtener MTLTexture desde SDL_Texture SDL3 renderiza el juego a `back_buffer_` (SDL_Texture). Necesitamos obtener la textura Metal subyacente para pasarla al fragment shader. **Opciones:** 1. **SDL_GetProperty()** - Usar SDL3 properties system: ```cpp id metal_texture = (__bridge id)SDL_GetProperty( SDL_GetTextureProperties(back_buffer_), "SDL.texture.metal.texture", nullptr ); ``` 2. **Render to Metal texture directamente** - En lugar de usar SDL_Texture, crear una MTLTexture directamente y renderizar el juego ahí. Más trabajo pero más control. 3. **Copiar SDL texture a Metal texture** - Menos eficiente pero más simple. ## Sampler State Falta crear el sampler state (equivalente a glTexParameteri en OpenGL): ```objc MTLSamplerDescriptor* sampler_descriptor = [[MTLSamplerDescriptor alloc] init]; sampler_descriptor.minFilter = MTLSamplerMinMagFilterLinear; sampler_descriptor.magFilter = MTLSamplerMinMagFilterLinear; sampler_descriptor.sAddressMode = MTLSamplerAddressModeClampToEdge; sampler_descriptor.tAddressMode = MTLSamplerAddressModeClampToEdge; id sampler_state = [device_ newSamplerStateWithDescriptor:sampler_descriptor]; ``` ## Build Configuration Para compilar en Xcode/CMake, necesitarás: 1. **CMakeLists.txt** - Añadir metal_shader.mm: ```cmake if(APPLE) set(RENDERING_SOURCES ${RENDERING_SOURCES} source/rendering/metal/metal_shader.mm ) target_link_libraries(${PROJECT_NAME} "-framework Metal" "-framework QuartzCore" ) endif() ``` 2. **Compilar shaders .metal** - Opcionalmente pre-compilar: ```bash xcrun -sdk macosx metal -c crtpi_vertex.metal -o crtpi_vertex.air xcrun -sdk macosx metal -c crtpi_fragment.metal -o crtpi_fragment.air xcrun -sdk macosx metallib crtpi_*.air -o crtpi.metallib ``` 3. **Cargar .metallib** en código: ```objc NSString* path = [[NSBundle mainBundle] pathForResource:@"crtpi" ofType:@"metallib"]; id library = [device_ newLibraryWithFile:path error:&error]; ``` ## Testing en macOS Cuando pruebes en macOS: 1. Verifica que `SDL_WINDOW_METAL` está activo en screen.cpp 2. Compila con `-DCMAKE_BUILD_TYPE=Debug` para ver logs 3. Usa Xcode Instruments (Metal Debugger) para inspeccionar frames 4. Compara rendimiento con/sin shaders ## Referencias Útiles - [Metal Programming Guide](https://developer.apple.com/metal/) - [Metal Shading Language Specification](https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) - [SDL3 Metal Integration](https://github.com/libsdl-org/SDL/blob/main/docs/README-metal.md) ## Próximos Pasos 1. Implementar `render()` completo 2. Resolver obtención de textura desde SDL 3. Crear sampler state 4. Testear en macOS real 5. Optimizar si es necesario (probablemente ya será rápido) --- **Nota importante**: Metal es significativamente más verboso que OpenGL pero también más eficiente. Una vez que funcione el render loop, el rendimiento debería ser excelente en macOS.