summaryrefslogtreecommitdiff
path: root/txtgameengine/app.py
blob: a8c9913794c0621bd0ef5f9ac7f6656fbae5a8cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import glfw
from vulkan import *
import time


class PlatformError(Exception):
    pass


class QueueFamilies:
    graphics_i: int
    graphics: object


class PlatformComponent:
    def __init__(self, app: 'TxtGameApp'):
        self.app = app

    def init(self):
        glfw.init()
        self.init_vulkan()
        self.init_window()

    def init_vulkan(self):
        self.create_instance()
        self.pick_physical_device()
        self.create_logical_device()

    def is_device_suitable(self, device):
        return 1

    def pick_physical_device(self):
        devices = [(d, self.is_device_suitable(d))
                   for d in vkEnumeratePhysicalDevices(self.instance)]
        if len(devices) == 0:
            raise PlatformError("No vulkan devices available.")
        device, rating = sorted(devices, key=lambda x: x[1])[-1]
        if rating < 0:
            raise PlatformError("No suitable devices available.")
        self.physical_device = device
        self.find_queue_families()

    def find_queue_families(self):
        self.queues = QueueFamilies()
        for i, queue_family in enumerate(vkGetPhysicalDeviceQueueFamilyProperties(self.physical_device)):
            if queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT:
                self.queues.graphics_i = i

    def create_logical_device(self):
        queue_create_info = [VkDeviceQueueCreateInfo(
            queueFamilyIndex=self.queues.graphics_i,
            queueCount=1,
            pQueuePriorities=[1],
            flags=0,
        )]
        device_create = VkDeviceCreateInfo(
            pQueueCreateInfos=queue_create_info,
            pEnabledFeatures=vkGetPhysicalDeviceFeatures(self.physical_device),
            flags=0,
            ppEnabledLayerNames=self.layers,
            ppEnabledExtensionNames=[],
        )
        self.logical_device = vkCreateDevice(
            self.physical_device, device_create, None)
        self.queues.graphic = vkGetDeviceQueue(
            device=self.logical_device,
            queueFamilyIndex=self.queues.graphics_i,
            queueIndex=0,
        )

    def create_instance(self):
        app_info = VkApplicationInfo(
            pApplicationName=self.app.name,
            applicationVersion=VK_MAKE_VERSION(1, 0, 0),
            pEngineName="TxtGameEngine",
            engineVersion=VK_MAKE_VERSION(1, 0, 0),
            apiVersion=VK_API_VERSION_1_0,
        )
        extensions = glfw.get_required_instance_extensions()
        present_layers = vkEnumerateInstanceLayerProperties()
        missing_layers = set(self.app.requested_validation_layers) - \
            set(l.layerName for l in present_layers)
        if missing_layers:
            raise PlatformError(
                "Missing validation layers: "+str(missing_layers))
        self.layers = self.app.requested_validation_layers
        createInfo = VkInstanceCreateInfo(
            pApplicationInfo=app_info,
            flags=0,
            enabledExtensionCount=len(extensions),
            ppEnabledExtensionNames=extensions,
            enabledLayerCount=len(self.layers),
            ppEnabledLayerNames=self.layers,
        )
        self.instance = vkCreateInstance(createInfo, None)

    def init_window(self):
        glfw.window_hint(glfw.CLIENT_API, glfw.NO_API)
        glfw.window_hint(glfw.RESIZABLE, glfw.FALSE)
        self.window = glfw.create_window(
            *self.app.size, self.app.name, None, None)
        if not self.window:
            raise PlatformError("Failed to initialize glfw window")

    @property
    def should_close(self) -> bool:
        return glfw.window_should_close(self.window)

    @should_close.setter
    def should_close(self, val: bool):
        glfw.set_window_should_close(val)

    def poll_events(self):
        glfw.poll_events()

    def cleanup(self):
        vkDestroyDevice(self.logical_device)
        vkDestroyInstance(self.instance)
        glfw.destroy_window(window)
        glfw.terminate()


class TxtGameApp:
    PLATFORM_CLASS = PlatformComponent

    def __init__(self, size: (int, int), name: str):
        self.size = size
        self.name = name
        self.platform: self.PLATFORM_CLASS = self.PLATFORM_CLASS(self)
        self.requested_validation_layers = []

    def start(self):
        self.platform.init()
        last_update_time = time.monotonic()
        while not self.platform.should_close:
            update_time = time.monotonic()
            self.platform.poll_events()
            self.update(update_time - last_update_time)
            last_update_time = update_time

    def update(self, delta: float):
        raise NotImplementedError