d6ffbda00d
- Shaders MSL portados desde GLSL (vertex + fragment) - Estructura básica de MetalShader class - Device, command queue, pipeline y buffers creados - CMakeLists.txt actualizado con Metal frameworks - assets.txt incluye shaders .metal como opcionales PENDIENTE: - Implementar render() loop completo - Obtener MTLTexture desde SDL_Texture - Crear sampler state - Testing en macOS real Ver METAL_BACKEND_NOTES.md para detalles de implementación.
167 lines
5.7 KiB
Markdown
167 lines
5.7 KiB
Markdown
# 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<CAMetalDrawable> drawable = [metal_layer_ nextDrawable];
|
|
|
|
2. Crear command buffer:
|
|
id<MTLCommandBuffer> 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<MTLRenderCommandEncoder> 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<MTLTexture> metal_texture = (__bridge id<MTLTexture>)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<MTLSamplerState> 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<MTLLibrary> 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.
|