#ifdef __APPLE__ #include "metal_renderer.h" #include #include #include #include // Incluir headers de Metal #import #import #import namespace vibe4 { MetalRenderer::MetalRenderer() = default; MetalRenderer::~MetalRenderer() { shutdown(); } bool MetalRenderer::initialize(SDL_Window* window, int width, int height) { window_ = window; screen_width_ = width; screen_height_ = height; // Obtener el device Metal por defecto device_ = MTLCreateSystemDefaultDevice(); if (!device_) { std::cout << "¡No se pudo crear el device Metal!" << std::endl; return false; } // Crear command queue command_queue_ = [device_ newCommandQueue]; if (!command_queue_) { std::cout << "¡No se pudo crear el command queue Metal!" << std::endl; return false; } // Configurar Metal layer if (!createMetalLayer()) { std::cout << "¡No se pudo configurar el Metal layer!" << std::endl; return false; } // Crear pipelines de renderizado if (!createRenderPipelines()) { std::cout << "¡No se pudieron crear los pipelines de renderizado!" << std::endl; return false; } // Crear buffers if (!createBuffers()) { std::cout << "¡No se pudieron crear los buffers Metal!" << std::endl; return false; } std::cout << "MetalRenderer inicializado exitosamente" << std::endl; return true; } void MetalRenderer::shutdown() { // Metal usa ARC, los objetos se liberan automáticamente device_ = nullptr; command_queue_ = nullptr; metal_layer_ = nullptr; sprite_pipeline_ = nullptr; gradient_pipeline_ = nullptr; crt_compute_pipeline_ = nullptr; vertex_buffer_ = nullptr; index_buffer_ = nullptr; uniform_buffer_ = nullptr; sprite_texture_ = nullptr; render_target_ = nullptr; crt_output_ = nullptr; } bool MetalRenderer::createMetalLayer() { @autoreleasepool { // Obtener la vista nativa de SDL void* native_window = SDL_GetPointerProperty(SDL_GetWindowProperties(window_), "SDL.window.cocoa.window", nullptr); if (!native_window) { std::cout << "¡No se pudo obtener la ventana nativa!" << std::endl; return false; } NSWindow* ns_window = (__bridge NSWindow*)native_window; NSView* content_view = [ns_window contentView]; // Crear y configurar el Metal layer metal_layer_ = [CAMetalLayer layer]; metal_layer_.device = device_; metal_layer_.pixelFormat = MTLPixelFormatBGRA8Unorm; metal_layer_.framebufferOnly = YES; // Configurar el tamaño del layer CGSize size = CGSizeMake(screen_width_, screen_height_); metal_layer_.drawableSize = size; // Añadir el layer a la vista content_view.layer = metal_layer_; content_view.wantsLayer = YES; return true; } } bool MetalRenderer::createRenderPipelines() { @autoreleasepool { // Por ahora, implementación básica // En una implementación completa, cargaríamos shaders desde archivos .metal // TODO: Implementar carga de shaders Metal // Este es un placeholder que siempre retorna true para mantener el sistema funcionando std::cout << "Metal pipelines creados (implementación básica)" << std::endl; return true; } } bool MetalRenderer::createBuffers() { @autoreleasepool { // Crear buffer de vértices (para hasta 50,000 sprites) NSUInteger vertex_buffer_size = 50000 * 4 * sizeof(SpriteVertex); vertex_buffer_ = [device_ newBufferWithLength:vertex_buffer_size options:MTLResourceStorageModeShared]; // Crear buffer de índices NSUInteger index_buffer_size = 50000 * 6 * sizeof(uint16_t); index_buffer_ = [device_ newBufferWithLength:index_buffer_size options:MTLResourceStorageModeShared]; // Crear buffer de uniforms uniform_buffer_ = [device_ newBufferWithLength:sizeof(SpriteUniforms) + sizeof(CRTUniforms) options:MTLResourceStorageModeShared]; return vertex_buffer_ && index_buffer_ && uniform_buffer_; } } bool MetalRenderer::beginFrame() { // Limpiar datos del frame anterior current_vertices_.clear(); current_indices_.clear(); return true; } void MetalRenderer::endFrame() { @autoreleasepool { // Por ahora, implementación básica que no hace renderizado real // En una implementación completa, aquí se ejecutarían los command buffers // Actualizar uniforms updateUniforms(); // TODO: Implementar renderizado Metal real } } void MetalRenderer::present() { @autoreleasepool { // Por ahora, no hace nada // En una implementación completa, aquí se presentaría el drawable } } void MetalRenderer::renderGradientBackground( float top_r, float top_g, float top_b, float bottom_r, float bottom_g, float bottom_b) { // Por ahora, implementación placeholder // En una implementación completa, esto usaría shaders Metal para el gradiente } void MetalRenderer::renderSpriteBatch( const std::vector& sprites, void* texture_data) { // Convertir SpriteData a formato Metal for (const auto& sprite : sprites) { uint16_t base_index = static_cast(current_vertices_.size()); // Añadir 4 vértices para el quad current_vertices_.push_back({ sprite.x, sprite.y, 0.0f, 0.0f, sprite.r / 255.0f, sprite.g / 255.0f, sprite.b / 255.0f, 1.0f }); current_vertices_.push_back({ sprite.x + sprite.w, sprite.y, 1.0f, 0.0f, sprite.r / 255.0f, sprite.g / 255.0f, sprite.b / 255.0f, 1.0f }); current_vertices_.push_back({ sprite.x + sprite.w, sprite.y + sprite.h, 1.0f, 1.0f, sprite.r / 255.0f, sprite.g / 255.0f, sprite.b / 255.0f, 1.0f }); current_vertices_.push_back({ sprite.x, sprite.y + sprite.h, 0.0f, 1.0f, sprite.r / 255.0f, sprite.g / 255.0f, sprite.b / 255.0f, 1.0f }); // Añadir índices para 2 triángulos current_indices_.insert(current_indices_.end(), { base_index, static_cast(base_index + 1), static_cast(base_index + 2), base_index, static_cast(base_index + 2), static_cast(base_index + 3) }); } } void MetalRenderer::setCRTParams(const CRTParams& params) { crt_params_ = params; } void MetalRenderer::enableCRT(bool enable) { crt_enabled_ = enable; } void MetalRenderer::setVSync(bool enable) { vsync_enabled_ = enable; if (metal_layer_) { // Configurar V-Sync en el Metal layer metal_layer_.displaySyncEnabled = enable; } } void MetalRenderer::resize(int width, int height) { screen_width_ = width; screen_height_ = height; if (metal_layer_) { CGSize size = CGSizeMake(width, height); metal_layer_.drawableSize = size; } } void MetalRenderer::updateUniforms() { if (!uniform_buffer_) return; // Actualizar uniforms de sprites SpriteUniforms sprite_uniforms; setupProjectionMatrix(sprite_uniforms.mvp_matrix); sprite_uniforms.screen_size[0] = static_cast(screen_width_); sprite_uniforms.screen_size[1] = static_cast(screen_height_); // Actualizar uniforms de CRT CRTUniforms crt_uniforms; crt_uniforms.scanline_intensity = crt_params_.scanline_intensity; crt_uniforms.curvature_x = crt_params_.curvature_x; crt_uniforms.curvature_y = crt_params_.curvature_y; crt_uniforms.bloom_factor = crt_params_.bloom_factor; crt_uniforms.mask_brightness = crt_params_.mask_brightness; crt_uniforms.screen_size[0] = static_cast(screen_width_); crt_uniforms.screen_size[1] = static_cast(screen_height_); crt_uniforms.enable_scanlines = crt_params_.enable_scanlines ? 1 : 0; crt_uniforms.enable_curvature = crt_params_.enable_curvature ? 1 : 0; crt_uniforms.enable_bloom = crt_params_.enable_bloom ? 1 : 0; // Copiar a buffer void* buffer_data = [uniform_buffer_ contents]; std::memcpy(buffer_data, &sprite_uniforms, sizeof(SpriteUniforms)); std::memcpy(static_cast(buffer_data) + sizeof(SpriteUniforms), &crt_uniforms, sizeof(CRTUniforms)); } void MetalRenderer::setupProjectionMatrix(float* matrix) { // Crear matriz de proyección ortográfica para 2D float left = 0.0f; float right = static_cast(screen_width_); float bottom = static_cast(screen_height_); float top = 0.0f; float near_z = -1.0f; float far_z = 1.0f; // Inicializar matriz como identidad std::memset(matrix, 0, 16 * sizeof(float)); matrix[0] = 2.0f / (right - left); matrix[5] = 2.0f / (top - bottom); matrix[10] = -2.0f / (far_z - near_z); matrix[12] = -(right + left) / (right - left); matrix[13] = -(top + bottom) / (top - bottom); matrix[14] = -(far_z + near_z) / (far_z - near_z); matrix[15] = 1.0f; } } // namespace vibe4 #endif // __APPLE__