Keep all features and logic but make it run only on GPU and make it auto install everyhting what you need to run it before it starting this musst be on DirectML on GPU no fallbacks it shoudl use the .pt file on GPU but all logic and everyhting els should work give me the entire thing back with everything no skiping i need the entire this they are all in one single .pt file so other threads rely on it.

# --- YOLO SINGLETON ---
class YOLOEnsemble:
    _instance = None
    _lock = threading.Lock()

    def __init__(self, models_config):
        self.models = []
        self.names = {}
        self.prediction_cache = {}
        self.cache_lock = threading.Lock()
        self.device = self._setup_directml_device()
        self._load_models(models_config)

    def _setup_directml_device(self):
        """Setup DirectML for maximum AMD GPU performance with fallbacks"""
        try:
            # Try DirectML first
            if torch_directml.is_available():
                device_count = torch_directml.device_count()
                logging.info(f"DirectML available with {device_count} device(s)")
                
                if device_count > 0:
                    # Get the best DirectML device (usually 0)
                    dml_device = torch_directml.device(0)
                    
                    # Maximum DirectML optimizations
                    torch.backends.directml.enabled = True
                    torch.backends.directml.allow_reduced_precision = True
                    
                    logging.info(f"DirectML device initialized: {dml_device}")
                    return dml_device
            
            # Fallback to CUDA if available
            if torch.cuda.is_available():
                cuda_device = torch.device('cuda:0')
                logging.info(f"CUDA device initialized: {cuda_device}")
                return cuda_device
            
            # Final fallback to CPU with warning
            logging.warning("No GPU available, falling back to CPU (performance will be reduced)")
            return torch.device('cpu')
                
        except Exception as e:
            logging.warning(f"GPU setup failed ({e}), falling back to CPU")
            return torch.device('cpu')

    def _load_models(self, models_config):
        """Load models with DirectML optimization for maximum performance"""
        for model_path, imgsz in models_config:
            try:
                # Load YOLO model
                model = YOLO(model_path)
                
                # Force model to DirectML device for 100% GPU utilization
                model.model = model.model.to(self.device)
                
                # Set model to evaluation mode and optimize
                model.model.eval()
                
                # Enable DirectML optimizations
                if hasattr(torch.backends, 'directml') and torch.backends.directml.enabled:
                    # Compile model for maximum DirectML performance
                    try:
                        model.model = torch.jit.optimize_for_inference(model.model)
                        logging.info(f"Model JIT optimized for DirectML: {model_path}")
                    except:
                        logging.info(f"JIT optimization skipped for: {model_path}")
                
                # Zero-latency warmup with DirectML - more aggressive
                dummy = torch.zeros((1, 3, imgsz, imgsz), dtype=torch.float32, device=self.device)
                
                with torch.no_grad():
                    # More aggressive warmup - 15 iterations for maximum DirectML optimization
                    for _ in range(15):
                        try:
                            _ = model.model(dummy)
                        except:
                            # Fallback warmup using predict
                            dummy_np = np.zeros((imgsz, imgsz, 3), dtype=np.uint8)
                            model.predict(
                                source=dummy_np,
                                imgsz=imgsz,
                                device=self.device,
                                verbose=False,
                                stream=False
                            )
                            break
                
                # Clear cache for maximum performance (if DirectML available)
                if hasattr(torch, 'directml') and str(self.device).startswith('privateuseone'):
                    torch.directml.empty_cache()
                elif torch.cuda.is_available() and self.device.type == 'cuda':
                    torch.cuda.empty_cache()
                
                self.models.append((model, imgsz))
                if hasattr(model, "names"):
                    self.names.update(model.names)
                    
                logging.info(f"DirectML model loaded: {model_path} -> {imgsz}px on {self.device}")
                
            except Exception as e:
                logging.error(f"Failed to load model {model_path}: {e}")

    def predict(self, frame, conf_thres=0.3):
        """Ultra-high performance DirectML prediction with zero latency"""
        all_results = []
        original_height, original_width = frame.shape[:2]
        
        # Process all models with DirectML for maximum GPU utilization
        with torch.no_grad():  # Disable gradients for inference speed
            for model, imgsz in self.models:
                try:
                    # Calculate scale factors for coordinate transformation
                    scale_x = original_width / imgsz
                    scale_y = original_height / imgsz
                    
                    # Ultra-fast resize with optimized interpolation
                    resized_frame = cv2.resize(frame, (imgsz, imgsz), interpolation=cv2.INTER_LINEAR)
                    
                    # Maximum performance prediction with DirectML
                    results = model.predict(
                        source=resized_frame,
                        save=False,
                        imgsz=imgsz,
                        conf=conf_thres,
                        iou=0.15,  # More aggressive NMS for speed
                        device=self.device,
                        half=False,  # DirectML works better with float32
                        verbose=False,
                        stream=False,
                        agnostic_nms=True,
                        max_det=40,  # Slightly higher detection count
                        classes=[0],  # Person class only
                        retina_masks=False
                    )[0]
                    
                    # Store transformation data
                    results.scale_x = scale_x
                    results.scale_y = scale_y
                    results.original_width = original_width
                    results.original_height = original_height
                    
                    all_results.append(results)
                    
                except Exception as e:
                    logging.warning(f"DirectML prediction failed: {e}")
                    continue
        
        # No cache clearing to maintain maximum GPU utilization
        # Only clear if memory is getting full
        if hasattr(torch, 'directml') and str(self.device).startswith('privateuseone'):
            pass  # Keep DirectML memory allocated
        elif torch.cuda.is_available() and self.device.type == 'cuda':
            pass  # Keep CUDA memory allocated
        return all_results

    @classmethod
    def get_instance(cls, models_config):
        with cls._lock:
            if cls._instance is None:
                cls._instance = YOLOEnsemble(models_config)
            return cls._instance

def clamp(value, min_val, max_val):
    """Zero-overhead clamp function"""
    return min_val if value < min_val else max_val if value > max_val else value

class DetectionThread(QtCore.QThread):
    detection_signal = QtCore.pyqtSignal(list, list, list, list, list)

    def __init__(self, settings):
        super().__init__()
        self.settings = settings
        self.running = False
        self.frame_lock = threading.Lock()
        
        # BitBlt setup
        self.user32 = ctypes.windll.user32
        self.gdi32 = ctypes.windll.gdi32
        self.dc = None
        self.memdc = None
        self.bitmap = None
        self.bitmapinfo = None
        
        # Ultra-fast tracker with minimal overhead
        self.tracker = Sort(max_age=10, min_hits=1, iou_threshold=0.2)
        self.frame_skip = 0
        self.last_detection_time = 0
        
        # Screen cache
        self.screen_width = 0
        self.screen_height = 0
        self.center_x = 0
        self.center_y = 0        # Load DirectML optimized models
        model_paths = [
            ("models/BestMain.pt", 96),
            ("models/yolo11n.pt", 128)
        ]
        

        self.yolo_ensemble = YOLOEnsemble.get_instance(model_paths)

    def setup_bitblt(self):
        """Initialize BitBlt capture resources"""
        try:
            # Get desktop window handle
            hwnd = self.user32.GetDesktopWindow()
            
            # Get DC and create compatible DC
            self.dc = self.user32.GetDC(hwnd)
            self.memdc = self.gdi32.CreateCompatibleDC(self.dc)
            
            # Create bitmap info structure
            class BITMAPINFOHEADER(ctypes.Structure):
                _fields_ = [
                    ("biSize", ctypes.c_uint32),
                    ("biWidth", ctypes.c_int),
                    ("biHeight", ctypes.c_int),
                    ("biPlanes", ctypes.c_short),
                    ("biBitCount", ctypes.c_short),
                    ("biCompression", ctypes.c_uint32),
                    ("biSizeImage", ctypes.c_uint32),
                    ("biXPelsPerMeter", ctypes.c_int),
                    ("biYPelsPerMeter", ctypes.c_int),
                    ("biClrUsed", ctypes.c_uint32),
                    ("biClrImportant", ctypes.c_uint32)
                ]

            self.bitmapinfo = BITMAPINFOHEADER()
            self.bitmapinfo.biSize = ctypes.sizeof(BITMAPINFOHEADER)
            self.bitmapinfo.biPlanes = 1
            self.bitmapinfo.biBitCount = 32
            self.bitmapinfo.biCompression = 0
            
            return True
            
        except Exception as e:
            logging.error(f"BitBlt setup failed: {e}")
            return False

    def capture_screen_region(self, x, y, width, height):
        """Capture screen region using BitBlt"""
        try:
            # Update bitmap info
            self.bitmapinfo.biWidth = width
            self.bitmapinfo.biHeight = -height  # Negative for top-down
            self.bitmapinfo.biSizeImage = width * height * 4
            
            # Create compatible bitmap
            self.bitmap = self.gdi32.CreateCompatibleBitmap(self.dc, width, height)
            self.gdi32.SelectObject(self.memdc, self.bitmap)
            
            # Perform BitBlt operation
            result = self.gdi32.BitBlt(
                self.memdc, 0, 0, width, height,
                self.dc, x, y,
                0x00CC0020  # SRCCOPY
            )
            
            if not result:
                raise Exception("BitBlt failed")
            
            # Get bitmap bits
            buffer_size = width * height * 4
            buffer = (ctypes.c_byte * buffer_size)()
            
            self.gdi32.GetDIBits(
                self.memdc, self.bitmap, 0, height,
                buffer, ctypes.byref(self.bitmapinfo), 0
            )
            
            # Convert to numpy array
            array = np.frombuffer(buffer, dtype=np.uint8)
            array = array.reshape(height, width, 4)
            
            # Cleanup current bitmap
            self.gdi32.DeleteObject(self.bitmap)
            
            return array
            
        except Exception as e:
            logging.error(f"BitBlt capture failed: {e}")
            return None

    def cleanup_bitblt(self):
        """Clean up BitBlt resources"""
        try:
            if self.bitmap:
                self.gdi32.DeleteObject(self.bitmap)
            if self.memdc:
                self.gdi32.DeleteDC(self.memdc)
            if self.dc:
                self.user32.ReleaseDC(None, self.dc)
        except Exception as e:
            logging.error(f"BitBlt cleanup failed: {e}")

    def fast_nms(self, boxes, scores, iou_threshold=0.25):
        """Ultra-fast NMS implementation with minimal overhead"""
        if len(boxes) == 0:
            return []

        # Convert to numpy arrays for vectorized operations
        boxes = np.asarray(boxes, dtype=np.float32)
        scores = np.asarray(scores, dtype=np.float32)

        # Get sorted indices by score (descending)
        indices = np.argsort(scores)[::-1]
        
        keep = []
        while indices.size > 0:
            # Take the highest scoring box
            current = indices[0]
            keep.append(current)
            
            if indices.size == 1:
                break
            
            # Calculate IoU with vectorized operations
            remaining_indices = indices[1:]
            current_box = boxes[current]
            remaining_boxes = boxes[remaining_indices]
            
            # Fast IoU calculation
            ious = self._fast_iou(current_box, remaining_boxes)
            
            # Keep only boxes with IoU below threshold
            indices = remaining_indices[ious <= iou_threshold]
        
        return keep
    
    def _fast_iou(self, box, boxes):
        """Vectorized IoU calculation with minimal overhead"""
        # Intersection coordinates
        x1 = np.maximum(box[0], boxes[:, 0])
        y1 = np.maximum(box[1], boxes[:, 1])
        x2 = np.minimum(box[2], boxes[:, 2])
        y2 = np.minimum(box[3], boxes[:, 3])
        
        # Intersection area
        intersection = np.maximum(0, x2 - x1) * np.maximum(0, y2 - y1)
        
        # Box areas
        box_area = (box[2] - box[0]) * (box[3] - box[1])
        boxes_area = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])
        
        # Union and IoU
        union = box_area + boxes_area - intersection
        return intersection / np.maximum(union, 1e-10)

    def transform_coordinates(self, boxes, crop_x, crop_y, scale_x, scale_y):
        """Zero-latency coordinate transformation"""
        transformed_boxes = []
        
        for box in boxes:
            x1, y1, x2, y2 = box
            
            # Scale and transform in one operation
            x1_screen = int(x1 * scale_x + crop_x)
            y1_screen = int(y1 * scale_y + crop_y)
            x2_screen = int(x2 * scale_x + crop_x)
            y2_screen = int(y2 * scale_y + crop_y)
            
            transformed_boxes.append((x1_screen, y1_screen, x2_screen, y2_screen))
            
        return transformed_boxes

    def run(self):
        """Main detection loop with BitBlt capture and DirectML, optimized for low-end fps only"""
        self.running = True
        
        if not self.yolo_ensemble.models:
            logging.error("No models loaded")
            return
            
        if not self.setup_bitblt():
            logging.error("BitBlt setup failed")
            return

        # Use only the smallest model for speed: assume first loaded model is smallest
        main_model, main_imgsz = self.yolo_ensemble.models[0]

        # Get screen dimensions
        self.screen_width = self.user32.GetSystemMetrics(0)  # SM_CXSCREEN
        self.screen_height = self.user32.GetSystemMetrics(1)  # SM_CYSCREEN
        self.center_x = self.screen_width >> 1
        self.center_y = self.screen_height >> 1
        
        logging.info(f"BitBlt detection started: {self.screen_width}x{self.screen_height}")
        
        target_fps = 500  # Target FPS for low-end devices
        frame_time = 0.0 / target_fps
        
        last_time = time.perf_counter()
        
        try:
            while self.running:
                current_time = time.perf_counter()
                elapsed = current_time - last_time

                # Enforce FPS cap with sleep
                if elapsed < frame_time:
                    time.sleep(frame_time - elapsed)

                last_time = time.perf_counter()

                # Calculate FOV and crop region
                fov_radius = clamp(self.settings.get('fov_radius', 150), 50,
                                 min(self.screen_width, self.screen_height) >> 1)
                
                crop_x = max(self.center_x - fov_radius, 0)
                crop_y = max(self.center_y - fov_radius, 0)
                crop_width = min(fov_radius * 2, self.screen_width - crop_x)
                crop_height = min(fov_radius * 2, self.screen_height - crop_y)
                
                if crop_width < 100 or crop_height < 100:
                    continue
                
                # Capture screen region using BitBlt
                frame = self.capture_screen_region(crop_x, crop_y, crop_width, crop_height)
                
                if frame is None or frame.size == 0:
                    continue
                
                # Convert color space
                frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGB)
                
                # Run detection with DirectML on single model only for speed
                conf_thres = clamp(self.settings.get('confidence', 0.3), 0.2, 0.5)  # lower to skip low confidence
                
                with torch.no_grad():
                    # Resize frame only once, no scaling jitter
                    resized_frame = cv2.resize(frame, (main_imgsz, main_imgsz), interpolation=cv2.INTER_LINEAR)
                    
                    results = main_model.predict(
                        source=resized_frame,
                        save=False,
                        imgsz=main_imgsz,
                        conf=conf_thres,
                        iou=0.3,  # Slightly relaxed IOU for speed
                        device=self.yolo_ensemble.device,
                        half=False,
                        verbose=False,
                        stream=False,
                        agnostic_nms=True,
                        max_det=10,  # lower max detection to reduce compute
                        classes=[0],  # Only person class
                        retina_masks=False
                    )[0]
                
                # Process detections
                detections = []
                if hasattr(results, 'boxes') and len(results.boxes) > 0:
                    boxes = results.boxes.xyxy.cpu().numpy()
                    confs = results.boxes.conf.cpu().numpy()
                    classes = results.boxes.cls.cpu().numpy()
                    
                    scale_x = crop_width / main_imgsz
                    scale_y = crop_height / main_imgsz
                    
                    # Convert to screen space coordinates
                    transformed_boxes = []
                    for box in boxes:
                        x1, y1, x2, y2 = box
                        x1_screen = int(x1 * scale_x + crop_x)
                        y1_screen = int(y1 * scale_y + crop_y)
                        x2_screen = int(x2 * scale_x + crop_x)
                        y2_screen = int(y2 * scale_y + crop_y)
                        transformed_boxes.append((x1_screen, y1_screen, x2_screen, y2_screen))
                    
                    for (x1_screen, y1_screen, x2_screen, y2_screen), conf, cls in zip(transformed_boxes, confs, classes):
                        if conf < conf_thres:
                            continue
                        # Basic bounds check
                        if (x1_screen < x2_screen and y1_screen < y2_screen and
                            x1_screen >= 0 and y1_screen >= 0 and
                            x2_screen <= self.screen_width and y2_screen <= self.screen_height):
                            detections.append((x1_screen, y1_screen, x2_screen, y2_screen, float(conf), "person"))
                
                # Sort detections by confidence (highest first)
                detections.sort(key=lambda x: x[4], reverse=True)
                
                # Emit detection signal with empty tracking info (no tracker)
                boxes = [(x1, y1, x2, y2) for (x1, y1, x2, y2, conf, label) in detections]
                confidences = [conf for (_, _, _, _, conf, _) in detections]
                labels = [label for (_, _, _, _, _, label) in detections]

                # Emit with empty track IDs and repeated labels as tracking is disabled
                self.detection_signal.emit(boxes, labels, confidences, labels, [0]*len(boxes))
                
        except Exception as e:
            logging.error(f"Detection error: {e}")
        
        finally:
            self.cleanup_bitblt()

    def stop(self):
        """Clean shutdown"""
        self.running = False
        self.cleanup_bitblt()
        logging.info("BitBlt detection stopped")




class AimAssistThread(QtCore.QThread):
    aim_active_signal = QtCore.pyqtSignal(bool)

    def __init__(self, settings, get_targets_func, get_fov_center_func):
        super().__init__()
        self.settings = settings
        self.get_targets = get_targets_func
        self.get_fov_center = get_fov_center_func
        self.running = False
        self.aim_active = False
        self.lock = threading.Lock()
        
        # Tracking settings
        self.locked_track_id = None
        self.target_acquired = False
        
        # Performance settings
        self.aim_fps = 1000  # Increased for faster response
        self.frame_time = 0.0 / self.aim_fps
        self.last_aim_time = 0
        
        # Anti-recoil settings
        self.recoil_control = True
        self.recoil_strength = 1.8  # Adjust as needed
        self.recoil_recovery = 0.90  # Recovery rate
        self.current_recoil = 0
        
        # Aim settings
        self.aim_smoothing = 0.80  # Lower = faster aim
        self.max_speed = 9  # Increased max speed
        self.deadzone = 1.0  # Reduced deadzone
        
        # Toggle controls
        self.toggle_key = 'p'
        self.aim_toggled_on = False
        self.last_toggle_time = 0
        self.toggle_cooldown = 0.30  # Faster toggle response

    def calculate_aim_point(self, x1, y1, x2, y2):
        """Fast aim point calculation with height-based targeting"""
        center_x = (x1 + x2) >> 1
        height = y2 - y1
        # Aim higher for distant targets (smaller height)
        aim_height = 0.2 if height < 100 else 0.3
        center_y = y1 + int(height * aim_height)  # Aim at upper chest/head
        return center_x, center_y

    def apply_anti_recoil(self, dy):
        """Apply dynamic anti-recoil compensation"""
        if not self.recoil_control:
            return dy
            
        if dy > 0:  # Only apply when aiming down
            self.current_recoil = min(self.current_recoil + 0.2, self.recoil_strength)
        else:
            self.current_recoil *= self.recoil_recovery
            
        return dy - (self.current_recoil * 2.0)

    def calculate_movement(self, target_pos, fc_x, fc_y):
        """Optimized movement calculation with anti-recoil"""
        if not target_pos:
            return 0, 0

        dx = target_pos[0] - fc_x
        dy = target_pos[1] - fc_y
        
        # Apply deadzone
        if abs(dx) <= self.deadzone and abs(dy) <= self.deadzone:
            return 0, 0

        # Calculate base movement
        move_x = dx * self.aim_smoothing
        move_y = dy * self.aim_smoothing
        
        # Apply anti-recoil to vertical movement
        move_y = self.apply_anti_recoil(move_y)
        
        # Speed limiting
        move_x = clamp(move_x, -self.max_speed, self.max_speed)
        move_y = clamp(move_y, -self.max_speed, self.max_speed)

        return int(move_x), int(move_y)

    def get_best_target(self, targets, fc_x, fc_y, fov_radius):
        """Fast target selection with priority system"""
        if not targets:
            return None, None
            
        best_target = None
        best_score = float('inf')
        fov_radius_sq = fov_radius * fov_radius
        
        for target in targets:
            if len(target) < 4:
                continue
                
            x1, y1, x2, y2 = target[:4]
            track_id = target[5] if len(target) > 5 else None
            
            # Fast center calculation
            center_x, center_y = self.calculate_aim_point(x1, y1, x2, y2)
            
            # Quick distance check
            dx = center_x - fc_x
            dy = center_y - fc_y
            dist_sq = dx*dx + dy*dy
            
            if dist_sq >= fov_radius_sq:
                continue
                
            # Prioritize current target
            if track_id == self.locked_track_id:
                return (center_x, center_y), target
                
            # Score based on distance and size
            target_size = (x2-x1) * (y2-y1)
            score = dist_sq / (target_size + 1)
            
            if score < best_score:
                best_score = score
                best_target = ((center_x, center_y), target)
                
        return best_target if best_target else (None, None)

    def run(self):
        """High-performance aim assist loop"""
        self.running = True
        sleep_time = 0.0001  # Minimal sleep
        
        while self.running:
            try:
                current_time = time.perf_counter()
                
                if current_time - self.last_aim_time < self.frame_time:
                    time.sleep(sleep_time)
                    continue

                self.last_aim_time = current_time

                # Fast toggle check
                if keyboard.is_pressed(self.toggle_key):
                    if current_time - self.last_toggle_time > self.toggle_cooldown:
                        self.aim_toggled_on = not self.aim_toggled_on
                        self.last_toggle_time = current_time

                aim_active = False

                if self.aim_toggled_on and self.settings.get('aim_enabled', True):
                    with self.lock:
                        targets = self.get_targets()

                    fov_center = self.get_fov_center()
                    
                    if targets and fov_center:
                        fc_x, fc_y = (fov_center.x(), fov_center.y()) if hasattr(fov_center, "x") else fov_center
                        fov_radius = self.settings.get('fov_radius', 200)
                        
                        target_pos, target_data = self.get_best_target(targets, fc_x, fc_y, fov_radius)
                        
                        if target_pos:
                            if not self.target_acquired:
                                self.target_acquired = True
                                if target_data and len(target_data) > 5:
                                    self.locked_track_id = target_data[5]
                            
                            mdx, mdy = self.calculate_movement(target_pos, fc_x, fc_y)
                            
                            if mdx != 0 or mdy != 0:
                                move_mouse_relative(mdx, mdy)
                                aim_active = True
                        else:
                            self.target_acquired = False
                            self.locked_track_id = None
                            self.current_recoil = 0
                
                if aim_active != self.aim_active:
                    self.aim_active = aim_active
                    self.aim_active_signal.emit(self.aim_active)

            except Exception as e:
                logging.error(f"AimAssist error: {e}")
                time.sleep(sleep_time)

    def stop(self):
        """Clean shutdown"""
        self.running = False
        self.target_acquired = False
        self.locked_track_id = None
        self.current_recoil = 0