Тема: [Пост] Як створити гру за допомогою python та javascript

Цей пост для тих хто хоче розібратися як виводити прості примітиви за допомогою python та javascript. Я написав невеличку гру, можливо комусь стане в нагоді для навчальних цілей. В цьому пості запитання немає, але якщо ви хочете, ви можете оптимізувати цей код

Вам потрібно створити просту гру за допомогою бібліотеки Pygame. Гра повинна мати наступні функції:

1. На екрані має з'являтися невеликий квадрат.
2. Квадрат має рухатися випадковим чином в будь-якому напрямку та з випадковою швидкістю. Він повинен мати можливість динамічно змінювати свою швидкість та напрямок під час гри.
3. Коли гравець натискає на квадрат, на екрані має з'являтися блакитний текст, який відображає кількість кліків на квадрат.
4. Якщо гравець промахує квадрат, на екрані має з'являтися червоний текст, який відображає кількість промахів.
5. Кожен раз, коли гравець натискає на квадрат, має звучати звук. Звуковий файл має бути закодований в Base64 та включений у файл гри.
6. Коли гравець натискає на квадрат, він має з'являтися випадково на екрані, за винятком області, де знаходиться курсор.

Для реалізації гри вам необхідно використовувати бібліотеку Pygame та створити один файл Python, який містить всі необхідні коди та ресурси (включаючи звуковий файл, закодований в Base64). Ви можете використовувати вбудовані функції Pygame для створення та переміщення квадрата, відтворення звуків та виявлення натискань миші. Ви також повинні використовувати функції генерації випадкових чисел для керування рухом та розміщенням квадрата. Нарешті, ви повинні відображати текст за допомогою функцій рендерингу шрифтів Pygame.

import pygame
import random
import math
import base64
import io

# Ініціалізуйте Pygame
pygame.init()

# Константи
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
SQUARE_SIZE = 50
FPS = 60
FONT_SIZE = 24

# Створіть вікно
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
clock = pygame.time.Clock()

# Приклад даних звуку, закодованих в base64
base64_sound = "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"

# Розшифруйте дані з base64 у бінарний формат
sound_data = base64.b64decode(base64_sound)

# Завантажте звук з бінарних даних в пам'ять
sound = pygame.mixer.Sound(file=io.BytesIO(sound_data))

# Створіть шрифт
font = pygame.font.SysFont(None, FONT_SIZE)

# Визначте клас квадратів
class Square(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((SQUARE_SIZE, SQUARE_SIZE))
        self.image.fill((255, 0, 0))
        self.rect = self.image.get_rect()
        self.rect.center = (random.randint(SQUARE_SIZE, WINDOW_WIDTH - SQUARE_SIZE), random.randint(SQUARE_SIZE, WINDOW_HEIGHT - SQUARE_SIZE))
        self.speed = random.randint(1, 10)
        self.direction = random.uniform(0, 2 * math.pi)

    def update(self):
        self.rect.move_ip(self.speed * math.cos(self.direction), self.speed * math.sin(self.direction))

        # Змінюйте напрямок та швидкість випадковим чином
        if random.random() < 0.01:
            self.speed = random.randint(1, 10)
        if random.random() < 0.01:
            self.direction = random.uniform(0, 2 * math.pi)

        # Відбивайте квадрати від меж екрану
        if self.rect.left < 0 or self.rect.right > WINDOW_WIDTH:
            self.direction = math.pi - self.direction
        if self.rect.top < 0 or self.rect.bottom > WINDOW_HEIGHT:
            self.direction = -self.direction

# Створіть спрайти
all_sprites = pygame.sprite.Group()
square = Square()
all_sprites.add(square)

# Визначте лічильники кліків та промахів
click_count = 0
miss_count = 0

# Головний цикл гри
running = True
while running:
    # Обробити події
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            # Перевірити, чи натиснуто на квадрат
            if square.rect.collidepoint(event.pos):
                click_count += 1
                sound.play()
                square.kill()
                square = Square()
                all_sprites.add(square)
            else:
                miss_count += 1

    # Оновити спрайти
    all_sprites.update()

    # Намалювати спрайти
    screen.fill((255, 255, 255))
    all_sprites.draw(screen)

    # Намалювати лічильники кліків і промахів
    click_text = font.render("Кліки: {}".format(click_count), True, (0, 0, 255))
    screen.blit(click_text, (10, 10))
    miss_text = font.render("Промахи: {}".format(miss_count), True, (255, 0, 0))
    screen.blit(miss_text, (10, FONT_SIZE + 10))

    # Оновити екран
    pygame.display.flip()

    # Зачекати на наступний кадр
    clock.tick(FPS)

# Вийти з Pygame
pygame.quit()
Подякували: koala, Firefox is dead2

2

Re: [Пост] Як створити гру за допомогою python та javascript

це Pixi.js, але поки що не дописав... потім допишу. Виявилося на JS набагато складніше ніж я думав
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Pixi.js Template</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.3/howler.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/6.1.3/browser/pixi.min.js"></script>
</head>
<body>
    <canvas id="gameCanvas"></canvas>
    <script>
        // Constants
        const WINDOW_WIDTH = 800;
        const WINDOW_HEIGHT = 600;
        const SQUARE_SIZE = 50;
        const FPS = 60;
        const FONT_SIZE = 24;

        // Create the Pixi.js application
        const app = new PIXI.Application({
            width: WINDOW_WIDTH,
            height: WINDOW_HEIGHT,
            backgroundColor: 0xffffff,
        });

        // Add the canvas to the HTML document
        document.body.appendChild(app.view);

        // Load the sound data from base64
        const base64_sound = "data:audio/mp3;base64,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";
        const sound_data = Uint8Array.from(atob(base64_sound.split(',')[1]), c => c.charCodeAt(0));

        // Create the sound
        const sound = new Howl({
            src: [sound_data],
        });

        // Create the font style
        const fontStyle = new PIXI.TextStyle({
            fontSize: FONT_SIZE,
            fill: "#0000ff",
        });

        // Define the Square class
        class Square extends PIXI.Sprite {
            constructor() {
                super(PIXI.Texture.WHITE);
                this.tint = 0xff0000;
                this.width = this.height = SQUARE_SIZE;
                this.anchor.set(0.5);
                this.position.set(
                    Math.random() * (WINDOW_WIDTH - SQUARE_SIZE) + SQUARE_SIZE / 2,
                    Math.random() * (WINDOW_HEIGHT - SQUARE_SIZE) + SQUARE_SIZE / 2
                );
                this.speed = Math.random() * 10 + 1;
                this.direction = Math.random() * Math.PI * 2;
            }

            update() {
                this.x += this.speed * Math.cos(this.direction);
                this.y += this.speed * Math.sin(this.direction);

                // Change the direction and speed randomly
                if (Math.random() < 0.01) {
                    this.speed = Math.random() * 10 + 1;
                }
                if (Math.random() < 0.01) {
                    this.direction = Math.random() * Math.PI * 2;
                }

                // Bounce the square off the edges of the screen
                if (this.left < 0 || this.right > WINDOW_WIDTH) {
                    this.direction = Math.PI - this.direction;
                }
                if (this.top < 0 || this.bottom > WINDOW_HEIGHT) {
                    this.direction = -this.direction;
                }
            }

            get left() {
                return this.x - this.width / 2;
            }

            get right() {
                return this.x + this.width / 2;
            }

            get top() {
                return this.y - this.height / 2;
            }

            get bottom() {
                return this.y + this.height / 2;
            }
        }

        // Create the sprites
        const allSprites = new PIXI.Container();
        const square = new Square();
        allSprites.addChild(square);

        // Define the click and miss counters
        let clickCount = 0;
        let missCount = 0;

        // Add the click and miss texts
        const clickText = new PIXI.Text("Clicks: 0", fontStyle);
        const missText = new PIXI.Text("Misses: 0", fontStyle);
        clickText.position.set(10, 10);
        missText.position.set(10, FONT_SIZE + 10);
        app.stage.addChild(clickText, missText);

        // Handle the game loop
        let lastTime = Date.now();
        function gameLoop() {
            // Calculate the time elapsed since the last frame
            const now = Date.now();
            const deltaTime = (now - lastTime) / 1000;
            lastTime = now;

            // Update the sprites
            allSprites.children.forEach((sprite) => {
                if (sprite.update) {
                    sprite.update(deltaTime);
                }
            });

            // Check for collisions with the square
            if (square.interactive && square.visible) {
                const mousePosition = app.renderer.plugins.interaction.mouse.global;
                if (
                    Math.abs(mousePosition.x - square.x) < square.width / 2 &&
                    Math.abs(mousePosition.y - square.y) < square.height / 2
                ) {
                    // The square was clicked
                    clickCount++;
                    clickText.text = `Clicks: ${clickCount}`;
                    sound.play();
                    square.visible = false;
                    square.interactive = false;
                    setTimeout(() => {
                        // Respawn the square after a delay
                        square.position.set(
                            Math.random() * (WINDOW_WIDTH - SQUARE_SIZE) + SQUARE_SIZE / 2,
                            Math.random() * (WINDOW_HEIGHT - SQUARE_SIZE) + SQUARE_SIZE / 2
                        );
                        square.speed = Math.random() * 10 + 1;
                        square.direction = Math.random() * Math.PI * 2;
                        square.visible = true;
                        square.interactive = true;
                    }, Math.random() * 3000 + 1000);
                }
            } else {
                // The square was missed
                missCount++;
                missText.text = `Misses: ${missCount}`;
                square.visible = false;
                square.interactive = false;
                setTimeout(() => {
                    // Respawn the square after a delay
                    square.position.set(
                        Math.random() * (WINDOW_WIDTH - SQUARE_SIZE) + SQUARE_SIZE / 2,
                        Math.random() * (WINDOW_HEIGHT - SQUARE_SIZE) + SQUARE_SIZE / 2
                    );
                    square.speed = Math.random() * 10 + 1;
                    square.direction = Math.random() * Math.PI * 2;
                    square.visible = true;
                    square.interactive = true;
                }, Math.random() * 3000 + 1000);
            }

            // Render the sprites
            app.renderer.render(app.stage);

            // Request the next frame
            requestAnimationFrame(gameLoop);
        }

        // Start the game loop
        requestAnimationFrame(gameLoop);
    </script>
</body>
</html>

3 Востаннє редагувалося Betterthanyou (01.03.2023 01:37:04)

Re: [Пост] Як створити гру за допомогою python та javascript

Приклад на p5. Але на жаль зі звуком не вийшло. Помилка: Not allowed to load local resource: blob:null/1fd4a8d4-030f-42b2-83c6-e020820bb225

Живий приклад: https://jsfiddle.net/Lgesh5xc/

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/molleindustria/p5.play/lib/p5.play.js"></script>
    <!--<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>-->
    <style>
        canvas {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <script> 
        //let soundFile;
        let squareSize = 50;   // size of the square
        let speed, angle;      // speed and angle of movement
        let clickCount = 0;    // number of clicks on the square
        let missCount = 0;     // number of misses

        //function preload() {
            //soundFormats('mp3');
            //soundFile = loadSound('https://assets.mixkit.co/active_storage/sfx/1662/1662-preview.mp3');
        //}

        function setup() {
            createCanvas(400, 400);
            resetSquare();
        }

        function draw() {
            background(255);  // clear the canvas

            // move the square
            squareX += speed * cos(angle);
            squareY += speed * sin(angle);

            // check if the square is out of bounds
            if (squareX < 0 || squareX > width - squareSize || squareY < 0 || squareY > height - squareSize) {
                // change the speed and direction of movement
                speed = random(1, 5);

                // check if the square is out of bounds
                if (squareX < 0) {
                    // change the angle to reflect off the left edge of the canvas
                    angle = PI - angle;
                } else if (squareX > width - squareSize) {
                    // change the angle to reflect off the right edge of the canvas
                    angle = PI - angle;
                }
                if (squareY < 0) {
                    // change the angle to reflect off the top edge of the canvas
                    angle = -angle;
                } else if (squareY > height - squareSize) {
                    // change the angle to reflect off the bottom edge of the canvas
                    angle = -angle;
                } 
            }

            // draw the square
            fill(255, 0, 0);
            rect(squareX, squareY, squareSize, squareSize);

            // display the click and miss counts
            fill(0, 0, 255);
            textSize(16);
            text("Clicks: " + clickCount, 10, 20);
            text("Misses: " + missCount, 10, 40);
        }

        function resetSquare() {
            // reset the square to a random location within the canvas bounds
            squareX = random(squareSize, width - squareSize);
            squareY = random(squareSize, height - squareSize);

            // make sure the square is not too close to the mouse
            while (dist(squareX, squareY, mouseX, mouseY) < 100) {
                squareX = random(squareSize, width - squareSize);
                squareY = random(squareSize, height - squareSize);
            }

            // set a random speed and direction
            speed = random(1, 5);
            angle = random(TWO_PI);
        }

        function mouseClicked() {
            // check if the mouse clicked on the square
            if (mouseX >= squareX && mouseX <= squareX + squareSize && mouseY >= squareY && mouseY <= squareY + squareSize) {
                //soundFile.play(); // play the sound
                clickCount++;   // increment the click count
                resetSquare();  // reset the square
            } else {
                missCount++;    // increment the miss count
            }
        } 
    </script>
</body>
</html>

4 Востаннє редагувалося Betterthanyou (01.03.2023 01:49:37)

Re: [Пост] Як створити гру за допомогою python та javascript

pygame надає просте в освоєнні крос-платформне рішення для створення 2D-ігор із вбудованою графікою, звуком і обробкою введення, що робить цю бібліотеку підходящою для початківців.

Pixi.js і p5 це js бібліотеки для створення 2D-ігор. Але на мій погляд вони зовсім не легкі. Можливо це із-за мови JavaScript.
З цих двох, би обрав p5 для простих задач.

Хочу наголосити що я просто вирішив знайти прості графічні бібліотеки для того щоб можна було щось графічне вивести, коли мені це потрібно. Звичайно що ви розробляє ігри для того щоб їх потім реалізувати, краще а шукати готові рішення такі як unity3D

Я розробляють ігри для того щоб тренувати нейронні мережі і візуально відображати що я роблю (keras / Reinforcement Learning)

5 Востаннє редагувалося Betterthanyou (01.03.2023 15:31:51)

Re: [Пост] Як створити гру за допомогою python та javascript

Звичайно є багато бібліотек, але найзручніша для мене це pygame

Ось приклад з OpenGL. Але писати гру на ній, я б не став, тому що надто складно

import numpy as np
from OpenGL.GL import *
import glfw

glfw.init()
# creating a window size having 900 as width and 700 as height
window = glfw.create_window(900, 700, "PyOpenGL Triangle", None,None)
glfw.set_window_pos(window, 500, 300)
glfw.make_context_current(window)

vertices = [-0.5, -0.5,0.0,
             0.5, -0.5,0.0,
             0.0, 0.5,0.0]

v = np.array(vertices, dtype = np.float32)

# this will draw a colorless triangle
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(3, GL_FLOAT,0,v)

# this will set a color for your background
glClearColor(255, 180, 0, 0)

while not glfw.window_should_close(window):
    glfw.poll_events()
    glClear(GL_COLOR_BUFFER_BIT)
    glDrawArrays(GL_TRIANGLES,0,3)
    glfw.swap_buffers(window)

glfw.terminate()

6

Re: [Пост] Як створити гру за допомогою python та javascript

Betterthanyou написав:

Звичайно є багато бібліотек, але не зручніше для мене це pygame

Ось приклад з OpenGL. Але писати гру на ній, я б не став, тому що надто складно

Так, тільки от технічно OpenGL - це не бібліотека. І дійсно, зазвичай ігри на чистому OpenGL ніхто не пише, частіше просто беруть готову бібліотеку чи ігровий рушій, які вже у своїх нутрощах самі викликають необхідні "АПІ". Це просто інший рівень абстракцій.

Подякували: Betterthanyou1

7

Re: [Пост] Як створити гру за допомогою python та javascript

wander написав:

Так, тільки от технічно OpenGL - це не бібліотека.

Хороше питання. Це стандарт бібліотек. L в OpenGL - це якраз Library.

Подякували: leofun01, Betterthanyou2

8 Востаннє редагувалося wander (02.03.2023 14:13:51)

Re: [Пост] Як створити гру за допомогою python та javascript

koala написав:

L в OpenGL - це якраз Library.

Так, якщо виходити лише з назви, то може здатися, що OpenGL - це бібліотека. Але технічно OpenGL - це лише специфікація.

https://uk.wikipedia.org/ написав:

OpenGL — специфікація, що визначає незалежний від мови програмування крос-платформовий програмний інтерфейс (API) для написання застосунків, що використовують 2D та 3D комп'ютерну графіку.

Про це також говориться на сторінці Khronos OpenGL® Registry:

Current OpenGL API, OpenGL Shading Language and GLX Specifications and Reference Pages

  • Current Specifications (OpenGL 4.6)

  • OpenGL 4.6 API Specification (May 5, 2022)

    • Core Profile Specification

    • Core Profile Specification with changes marked

    • Compatibility Profile Specification

    • Compatibility Profile Specification with changes marked

  • OpenGL Shading Language 4.60 Specification (July 10, 2019)

Це те, що на папері. На практиці все трохи цікавіше. Можна сказати, що OpenGL — це специфікація для розробників відеокарт, для створення своїх графічних драйверів. Якщо виробник вашої відеокарти створив драйвер, який відповідає певній версії OpenGL, ви можете отримати доступ до цього драйвера через OpenGL виклики. Виглядати це може десь так: йде виклик OpenGL функції, після чого у гру вступає деяка shared library (opengl32.dll/libGL.so/etc)*, яка перенаправить виклик до фактичного IHV драйвера**, а він вже перетворить виклик у hardware-specific команди. Так, насправді все трохи складніше, але в цілому це виглядає десь так.

* - Однак така бібліотека не містить реалізації ОпенГЛьної специфікації, а виступає лише як trampoline;
** - Цей виклик буде перенаправлено до OpenGL ICD на Windows/OpenGL Framework на MacOS/що там у Linux я хз, який вже містить реалізацію OpenGL.

Тож можна звісно сказати, що це "бібліотека", але тут багато нюансів, які можуть залежати від заліза/ОС/тощо.

Подякували: Tarpan87, leofun012

9

Re: [Пост] Як створити гру за допомогою python та javascript

Цікаво було дізнатися чи DirectX буде працювати з пітоном. Зіткнувся з помилкою No module named 'pywin32'. Вже все перевстановлював та перевіряв... не допомагає. Можливо пізніше розберуся
import win32api
import win32con
import win32gui
import struct
import ctypes
from ctypes import wintypes
import pywin32.directx.direct3d9 as d3d9

# define the window class
class Window:
    def __init__(self):
        self.width = 640
        self.height = 480
        self.title = "DirectX Window"
        self.hwnd = None
        self.d3d = None
        self.device = None
        self.vertex_buffer = None
        self.vertex_declaration = None
        self.indices = None

    def create_window(self):
        # define the window class
        wc = win32gui.WNDCLASS()
        wc.lpfnWndProc = self.wnd_proc
        wc.lpszClassName = self.title
        wc.style = win32con.CS_HREDRAW | win32con.CS_VREDRAW
        wc.hbrBackground = win32api.GetStockObject(win32con.WHITE_BRUSH)
        # register the window class
        win32gui.RegisterClass(wc)
        # create the window
        self.hwnd = win32gui.CreateWindow(
            self.title, self.title,
            win32con.WS_OVERLAPPEDWINDOW,
            win32con.CW_USEDEFAULT, win32con.CW_USEDEFAULT,
            self.width, self.height,
            None, None, None, None)
        # show the window
        win32gui.ShowWindow(self.hwnd, win32con.SW_SHOW)
        win32gui.UpdateWindow(self.hwnd)

    def init_d3d(self):
        self.d3d = d3d9.Direct3D()
        present_params = d3d9.PRESENT_PARAMETERS()
        present_params.BackBufferFormat = d3d9.FORMAT.A8R8G8B8
        present_params.BackBufferWidth = self.width
        present_params.BackBufferHeight = self.height
        self.device = self.d3d.CreateDevice(
            d3d9.ADAPTER_DEFAULT, d3d9.DEVTYPE_HAL,
            self.hwnd, d3d9.CREATE_HARDWARE_VERTEXPROCESSING,
            present_params)
        # create the vertex buffer
        vertices = [(0.0, 0.0, 0.0, 0xffff0000)]
        self.vertex_buffer = self.device.CreateVertexBuffer(
            1 * 16, d3d9.USAGE_WRITEONLY, 0, d3d9.POOL_MANAGED)
        self.vertex_buffer.SetData(vertices)
        # create the vertex declaration
        elements = [(0, 0, d3d9.DECLTYPE.FLOAT4, d3d9.DECLMETHOD.DEFAULT,
                     d3d9.DECLUSAGE.POSITION, 0)]
        self.vertex_declaration = self.device.CreateVertexDeclaration(elements)
        # create the index buffer
        self.indices = self.device.CreateIndexBuffer(
            3 * 2, d3d9.USAGE_WRITEONLY, d3d9.INDEX32, d3d9.POOL_MANAGED)
        self.indices.SetData([0, 1, 2])


    def render(self):
        # clear the screen
        self.device.Clear(0, None, d3d9.CLEAR_TARGET, 0xffcccccc, 0, 0)
        # begin the scene
        self.device.BeginScene()
        # set the vertex declaration
        self.device.SetVertexDeclaration(self.vertex_declaration)
        # set the vertex buffer
        self.device.SetStreamSource(0, self.vertex_buffer, 0)
        # set the index buffer
        self.device.SetIndices(self.indices)
        # draw the circle
        self.device.DrawIndexedPrimitive(
            d3d9.PRIMITIVE_TRIANGLELIST, 0, 0, 1, 0, 1)
        # end the scene
        self.device.EndScene()
        # present the backbuffer
        self.device.Present(None, None, None, None)


        # set the index buffer
        self.device.SetIndices(self.indices)
        # draw the circle
        self.device.DrawIndexedPrimitive(
            d3d9.PRIMITIVE_TRIANGLELIST, 0, 0, 1, 0, 1)
        # end the scene
        self.device.EndScene()
        # present the backbuffer
        self.device.Present(None, None, None, None)

    def wnd_proc(self, hwnd, msg, wParam, lParam):
        if msg == win32con.WM_PAINT:
            self.render()
            win32gui.ValidateRect(hwnd, None)
        elif msg == win32con.WM_DESTROY:
            win32gui.PostQuitMessage(0)
        else:
            return win32gui.DefWindowProc(hwnd, msg, wParam, lParam)
        return 0

    def run(self):
        # create the window
        self.create_window()
        # initialize Direct3D
        self.init_d3d()
        # enter the message loop
        while True:
            msg = win32gui.GetMessage(None, 0, 0)
            if msg[0] == 0:
                break
            win32gui.TranslateMessage(msg)
            win32gui.DispatchMessage(msg)

if __name__ == "__main__":
    window = Window()
    window.run()

10

Re: [Пост] Як створити гру за допомогою python та javascript

Як я розумію, pywin32 - це назва проєкту, а бібліотеки з нього звуться "win32api", "win32gui" і т.д. Ну і нашвидкуруч не можу нагуглити розширення directx з нього. Для directx є окремі проєкти.

Подякували: Betterthanyou1

11 Востаннє редагувалося Betterthanyou (02.03.2023 12:22:03)

Re: [Пост] Як створити гру за допомогою python та javascript

https://github.com/realitix/vulkan
vulkan API

# port from https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation
# glfw example.
#
# glfw version: https://github.com/FlorianRhiem/pyGLFW
# tested with python 2 and 3 on Windows 10

import os

from vulkan import *

import glfw


WIDTH = 800
HEIGHT = 600

validationLayers = ["VK_LAYER_LUNARG_standard_validation"]
deviceExtensions = [VK_KHR_SWAPCHAIN_EXTENSION_NAME]

enableValidationLayers = True


def debugCallback(*args):
    print('DEBUG: {} {}'.format(args[5], args[6]))
    return 0

def createDebugReportCallbackEXT(instance, pCreateInfo, pAllocator):
    func = vkGetInstanceProcAddr(instance, 'vkCreateDebugReportCallbackEXT')
    if func:
        return func(instance, pCreateInfo, pAllocator)
    else:
        return VK_ERROR_EXTENSION_NOT_PRESENT

def destroyDebugReportCallbackEXT(instance, callback, pAllocator):
    func = vkGetInstanceProcAddr(instance, 'vkDestroyDebugReportCallbackEXT')
    if func:
        func(instance, callback, pAllocator)

def destroySurface(instance, surface, pAllocator=None):
    func = vkGetInstanceProcAddr(instance, 'vkDestroySurfaceKHR')
    if func:
        func(instance, surface, pAllocator)

def destroySwapChain(device, swapChain, pAllocator=None):
    func = vkGetDeviceProcAddr(device, 'vkDestroySwapchainKHR')
    if func:
        func(device, swapChain, pAllocator)


class QueueFamilyIndices(object):

    def __init__(self):
        self.graphicsFamily = -1
        self.presentFamily = -1

    def isComplete(self):
        return self.graphicsFamily >= 0 and self.presentFamily >= 0


class SwapChainSupportDetails(object):
    def __init__(self):
        self.capabilities = None
        self.formats = None
        self.presentModes = None

class HelloTriangleApplication(object):

    def __init__(self):
        self.__window = None
        self.__instance = None
        self.__callback = None
        self.__surface = None
        self.__physicalDevice = None
        self.__device = None
        self.__graphicsQueue = None
        self.__presentQueue = None

        self.__swapChain = None
        self.__swapChainImages = None
        self.__swapChainImageFormat = None
        self.__swapChainExtent = None

        self.__swapChainImageViews = None
        self.__swapChainFramebuffers = None

        self.__renderPass = None
        self.__pipelineLayout = None
        self.__graphicsPipeline = None

        self.__commandPool = None
        self.__commandBuffers = None

        self.__imageAvailableSemaphore = None
        self.__renderFinishedSemaphore = None

    def __del__(self):
        vkDeviceWaitIdle(self.__device)

        if self.__imageAvailableSemaphore:
            vkDestroySemaphore(self.__device, self.__imageAvailableSemaphore, None)

        if self.__renderFinishedSemaphore:
            vkDestroySemaphore(self.__device, self.__renderFinishedSemaphore, None)

        if self.__commandBuffers:
            self.__commandBuffers = None

        if self.__commandPool:
            vkDestroyCommandPool(self.__device, self.__commandPool, None)

        if self.__swapChainFramebuffers:
            for i in self.__swapChainFramebuffers:
                vkDestroyFramebuffer(self.__device, i, None)
            self.__swapChainFramebuffers = None

        if self.__renderPass:
            vkDestroyRenderPass(self.__device, self.__renderPass, None)

        if self.__pipelineLayout:
            vkDestroyPipelineLayout(self.__device, self.__pipelineLayout, None)

        if self.__graphicsPipeline:
            vkDestroyPipeline(self.__device, self.__graphicsPipeline, None)

        if self.__swapChainImageViews:
            for i in self.__swapChainImageViews:
                vkDestroyImageView(self.__device, i, None)

        if self.__swapChain:
            destroySwapChain(self.__device, self.__swapChain, None)

        if self.__device:
            vkDestroyDevice(self.__device, None)

        if self.__surface:
            destroySurface(self.__instance, self.__surface, None)

        if self.__callback:
            destroyDebugReportCallbackEXT(self.__instance, self.__callback, None)

        if self.__instance:
            vkDestroyInstance(self.__instance, None)

    def __initWindow(self):
        glfw.init()

        glfw.window_hint(glfw.CLIENT_API, glfw.NO_API)
        glfw.window_hint(glfw.RESIZABLE, False)

        self.__window = glfw.create_window(WIDTH, HEIGHT, "Vulkan", None, None)

    def __initVulkan(self):
        self.__createInstance()
        self.__setupDebugCallback()
        self.__createSurface()
        self.__pickPhysicalDevice()
        self.__createLogicalDevice()
        self.__createSwapChain()
        self.__createImageViews()
        self.__createRenderPass()
        self.__createGraphicsPipeline()
        self.__createFramebuffers()
        self.__createCommandPool()
        self.__createCommandBuffers()
        self.__createSemaphores()

    def __mainLoop(self):
        while not glfw.window_should_close(self.__window):
            glfw.poll_events()
            self.__drawFrame()

    def __createInstance(self):
        if enableValidationLayers and not self.__checkValidationLayerSupport():
            raise Exception("validation layers requested, but not available!")

        appInfo = VkApplicationInfo(
            sType=VK_STRUCTURE_TYPE_APPLICATION_INFO,
            pApplicationName='Hello Triangle',
            applicationVersion=VK_MAKE_VERSION(1, 0, 0),
            pEngineName='No Engine',
            engineVersion=VK_MAKE_VERSION(1, 0, 0),
            apiVersion=VK_MAKE_VERSION(1, 0, 3)
        )

        createInfo = None
        extensions = self.__getRequiredExtensions()

        if enableValidationLayers:
            createInfo = VkInstanceCreateInfo(
                sType=VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
                pApplicationInfo=appInfo,
                enabledExtensionCount=len(extensions),
                ppEnabledExtensionNames=extensions,
                enabledLayerCount=len(validationLayers),
                ppEnabledLayerNames=validationLayers
            )
        else:
            createInfo = VkInstanceCreateInfo(
                sType=VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
                pApplicationInfo=appInfo,
                enabledExtensionCount=len(extensions),
                ppEnabledExtensionNames=extensions,
                enabledLayerCount=0
            )

        self.__instance = vkCreateInstance(createInfo, None)

    def __setupDebugCallback(self):
        if not enableValidationLayers:
            return

        createInfo = VkDebugReportCallbackCreateInfoEXT(
            sType=VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
            flags=VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT,
            pfnCallback=debugCallback
        )
        self.__callback = createDebugReportCallbackEXT(self.__instance, createInfo, None)
        if not self.__callback:
            raise Exception("failed to set up debug callback!")

    def __createSurface(self):
        surface_ptr = ffi.new('VkSurfaceKHR[1]')
        glfw.create_window_surface(self.__instance, self.__window, None, surface_ptr)
        self.__surface = surface_ptr[0]
        if self.__surface is None:
            raise Exception("failed to create window surface!")

    def __pickPhysicalDevice(self):
        devices = vkEnumeratePhysicalDevices(self.__instance)

        for device in devices:
            if self.__isDeviceSuitable(device):
                self.__physicalDevice = device
                break

        if self.__physicalDevice is None:
            raise Exception("failed to find a suitable GPU!")

    def __createLogicalDevice(self):
        indices = self.__findQueueFamilies(self.__physicalDevice)
        uniqueQueueFamilies = {}.fromkeys((indices.graphicsFamily, indices.presentFamily))
        queueCreateInfos = []
        for queueFamily in uniqueQueueFamilies:
            queueCreateInfo = VkDeviceQueueCreateInfo(
                sType=VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                queueFamilyIndex=queueFamily,
                queueCount=1,
                pQueuePriorities=[1.0]
            )
            queueCreateInfos.append(queueCreateInfo)

        deviceFeatures = VkPhysicalDeviceFeatures()
        createInfo = None
        if enableValidationLayers:
            createInfo = VkDeviceCreateInfo(
                sType=VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                flags=0,
                pQueueCreateInfos=queueCreateInfos,
                queueCreateInfoCount=len(queueCreateInfos),
                pEnabledFeatures=[deviceFeatures],
                enabledExtensionCount=len(deviceExtensions),
                ppEnabledExtensionNames=deviceExtensions,
                enabledLayerCount=len(validationLayers),
                ppEnabledLayerNames=validationLayers
            )
        else:
            createInfo = VkDeviceCreateInfo(
                sType=VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                flags=0,
                pQueueCreateInfos=queueCreateInfos,
                queueCreateInfoCount=len(queueCreateInfos),
                pEnabledFeatures=[deviceFeatures],
                enabledExtensionCount=len(deviceExtensions),
                ppEnabledExtensionNames=deviceExtensions,
                enabledLayerCount=0
            )

        self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None)
        if self.__device is None:
            raise Exception("failed to create logical device!")
        self.__graphicsQueue = vkGetDeviceQueue(self.__device, indices.graphicsFamily, 0)
        self.__presentQueue = vkGetDeviceQueue(self.__device, indices.presentFamily, 0)

    def __createSwapChain(self):
        swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice)

        surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats)
        presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes)
        extent = self.__chooseSwapExtent(swapChainSupport.capabilities)

        imageCount = swapChainSupport.capabilities.minImageCount + 1
        if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount:
            imageCount = swapChainSupport.capabilities.maxImageCount

        createInfo = VkSwapchainCreateInfoKHR(
            sType=VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
            flags=0,
            surface=self.__surface,
            minImageCount=imageCount,
            imageFormat=surfaceFormat.format,
            imageColorSpace=surfaceFormat.colorSpace,
            imageExtent=extent,
            imageArrayLayers=1,
            imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
        )

        indices = self.__findQueueFamilies(self.__physicalDevice)
        if indices.graphicsFamily != indices.presentFamily:
            createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT
            createInfo.queueFamilyIndexCount = 2
            createInfo.pQueueFamilyIndices = [indices.graphicsFamily, indices.presentFamily]
        else:
            createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE

        createInfo.preTransform = swapChainSupport.capabilities.currentTransform
        createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
        createInfo.presentMode = presentMode
        createInfo.clipped = True

        vkCreateSwapchainKHR = vkGetDeviceProcAddr(self.__device, 'vkCreateSwapchainKHR')
        self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None)

        vkGetSwapchainImagesKHR = vkGetDeviceProcAddr(self.__device, 'vkGetSwapchainImagesKHR')
        self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain)

        self.__swapChainImageFormat = surfaceFormat.format
        self.__swapChainExtent = extent

    def __createImageViews(self):
        self.__swapChainImageViews = []
        components = VkComponentMapping(VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
                                        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY)
        subresourceRange = VkImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT,
                                                   0, 1, 0, 1)
        for i, image in enumerate(self.__swapChainImages):
            createInfo = VkImageViewCreateInfo(
                sType=VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
                flags=0,
                image=image,
                viewType=VK_IMAGE_VIEW_TYPE_2D,
                format=self.__swapChainImageFormat,
                components=components,
                subresourceRange=subresourceRange
            )
            self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None))

    def __createRenderPass(self):
        colorAttachment = VkAttachmentDescription(
            format=self.__swapChainImageFormat,
            samples=VK_SAMPLE_COUNT_1_BIT,
            loadOp=VK_ATTACHMENT_LOAD_OP_CLEAR,
            storeOp=VK_ATTACHMENT_STORE_OP_STORE,
            stencilLoadOp=VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            stencilStoreOp=VK_ATTACHMENT_STORE_OP_DONT_CARE,
            initialLayout=VK_IMAGE_LAYOUT_UNDEFINED,
            finalLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
        )

        colorAttachmentRef = VkAttachmentReference(
            attachment=0,
            layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        )

        subPass = VkSubpassDescription(
            pipelineBindPoint=VK_PIPELINE_BIND_POINT_GRAPHICS,
            colorAttachmentCount=1,
            pColorAttachments=colorAttachmentRef
        )

        renderPassInfo = VkRenderPassCreateInfo(
            sType=VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
            attachmentCount=1,
            pAttachments=colorAttachment,
            subpassCount=1,
            pSubpasses=subPass
        )

        self.__renderPass = vkCreateRenderPass(self.__device, renderPassInfo, None)

    def __createGraphicsPipeline(self):
        path = os.path.dirname(os.path.abspath(__file__))
        vertShaderModule = self.__createShaderModule(os.path.join(path, 'hello_triangle_vert.spv'))
        fragShaderModule = self.__createShaderModule(os.path.join(path, 'hello_triangle_frag.spv'))

        vertShaderStageInfo = VkPipelineShaderStageCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
            flags=0,
            stage=VK_SHADER_STAGE_VERTEX_BIT,
            module=vertShaderModule,
            pName='main'
        )

        fragShaderStageInfo = VkPipelineShaderStageCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
            flags=0,
            stage=VK_SHADER_STAGE_FRAGMENT_BIT,
            module=fragShaderModule,
            pName='main'
        )

        shaderStages = [vertShaderStageInfo, fragShaderStageInfo]

        vertexInputInfo = VkPipelineVertexInputStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
            vertexBindingDescriptionCount=0,
            vertexAttributeDescriptionCount=0
        )

        inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
            topology=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
            primitiveRestartEnable=True
        )

        viewport = VkViewport(0.0, 0.0,
                              float(self.__swapChainExtent.width),
                              float(self.__swapChainExtent.height),
                              0.0, 1.0)
        scissor = VkRect2D([0, 0], self.__swapChainExtent)
        viewportState = VkPipelineViewportStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
            viewportCount=1,
            pViewports=viewport,
            scissorCount=1,
            pScissors=scissor
        )

        rasterizer = VkPipelineRasterizationStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
            depthClampEnable=False,
            rasterizerDiscardEnable=False,
            polygonMode=VK_POLYGON_MODE_FILL,
            lineWidth=1.0,
            cullMode=VK_CULL_MODE_BACK_BIT,
            frontFace=VK_FRONT_FACE_CLOCKWISE,
            depthBiasEnable=False
        )

        multisampling = VkPipelineMultisampleStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
            sampleShadingEnable=False,
            rasterizationSamples=VK_SAMPLE_COUNT_1_BIT
        )

        colorBlendAttachment = VkPipelineColorBlendAttachmentState(
            colorWriteMask=VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
            blendEnable=False
        )

        colorBlending = VkPipelineColorBlendStateCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
            logicOpEnable=False,
            logicOp=VK_LOGIC_OP_COPY,
            attachmentCount=1,
            pAttachments=colorBlendAttachment,
            blendConstants=[0.0, 0.0, 0.0, 0.0]
        )

        pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
            sType=VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
            setLayoutCount=0,
            pushConstantRangeCount=0
        )

        self.__pipelineLayout = vkCreatePipelineLayout(self.__device, pipelineLayoutInfo, None)

        pipelineInfo = VkGraphicsPipelineCreateInfo(
            sType=VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
            stageCount=2,
            pStages=shaderStages,
            pVertexInputState=vertexInputInfo,
            pInputAssemblyState=inputAssembly,
            pViewportState=viewportState,
            pRasterizationState=rasterizer,
            pMultisampleState=multisampling,
            pColorBlendState=colorBlending,
            layout=self.__pipelineLayout,
            renderPass=self.__renderPass,
            subpass=0
        )

        self.__graphicsPipeline = vkCreateGraphicsPipelines(self.__device, VK_NULL_HANDLE, 1, pipelineInfo, None)[0]

        vkDestroyShaderModule(self.__device, vertShaderModule, None)
        vkDestroyShaderModule(self.__device, fragShaderModule, None)

    def __createFramebuffers(self):
        self.__swapChainFramebuffers = []

        for imageView in self.__swapChainImageViews:
            attachments = [imageView,]

            framebufferInfo = VkFramebufferCreateInfo(
                sType=VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                renderPass=self.__renderPass,
                attachmentCount=1,
                pAttachments=attachments,
                width=self.__swapChainExtent.width,
                height=self.__swapChainExtent.height,
                layers=1
            )
            framebuffer = vkCreateFramebuffer(self.__device, framebufferInfo, None)
            self.__swapChainFramebuffers.append(framebuffer)

    def __createCommandPool(self):
        queueFamilyIndices = self.__findQueueFamilies(self.__physicalDevice)

        poolInfo = VkCommandPoolCreateInfo(
            sType=VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
            queueFamilyIndex=queueFamilyIndices.graphicsFamily
        )

        self.__commandPool = vkCreateCommandPool(self.__device, poolInfo, None)

    def __createCommandBuffers(self):
        # self.__commandBuffers = []

        allocInfo = VkCommandBufferAllocateInfo(
            sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
            commandPool=self.__commandPool,
            level=VK_COMMAND_BUFFER_LEVEL_PRIMARY,
            commandBufferCount=len(self.__swapChainFramebuffers)
        )

        commandBuffers = vkAllocateCommandBuffers(self.__device, allocInfo)
        self.__commandBuffers = [ffi.addressof(commandBuffers, i)[0] for i in range(len(self.__swapChainFramebuffers))]

        for i, cmdBuffer in enumerate(self.__commandBuffers):
            beginInfo = VkCommandBufferBeginInfo(
                sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
                flags=VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT
            )

            vkBeginCommandBuffer(cmdBuffer, beginInfo)

            renderPassInfo = VkRenderPassBeginInfo(
                sType=VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
                renderPass=self.__renderPass,
                framebuffer=self.__swapChainFramebuffers[i],
                renderArea=[[0, 0], self.__swapChainExtent]
            )

            clearColor = VkClearValue([[0.0, 0.0, 0.0, 1.0]])
            renderPassInfo.clearValueCount = 1
            renderPassInfo.pClearValues = ffi.addressof(clearColor)

            vkCmdBeginRenderPass(cmdBuffer, renderPassInfo, VK_SUBPASS_CONTENTS_INLINE)

            vkCmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, self.__graphicsPipeline)
            vkCmdDraw(cmdBuffer, 3, 1, 0, 0)

            vkCmdEndRenderPass(cmdBuffer)

            vkEndCommandBuffer(cmdBuffer)

    def __createSemaphores(self):
        semaphoreInfo = VkSemaphoreCreateInfo(sType=VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO)

        self.__imageAvailableSemaphore = vkCreateSemaphore(self.__device, semaphoreInfo, None)
        self.__renderFinishedSemaphore = vkCreateSemaphore(self.__device, semaphoreInfo, None)

    def __drawFrame(self):
        vkAcquireNextImageKHR = vkGetDeviceProcAddr(self.__device, 'vkAcquireNextImageKHR')
        vkQueuePresentKHR = vkGetDeviceProcAddr(self.__device, 'vkQueuePresentKHR')

        imageIndex = vkAcquireNextImageKHR(self.__device, self.__swapChain, 18446744073709551615,
                                           self.__imageAvailableSemaphore, VK_NULL_HANDLE)

        submitInfo = VkSubmitInfo(sType=VK_STRUCTURE_TYPE_SUBMIT_INFO)

        waitSemaphores = ffi.new('VkSemaphore[]', [self.__imageAvailableSemaphore])
        waitStages = ffi.new('uint32_t[]', [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, ])
        submitInfo.waitSemaphoreCount = 1
        submitInfo.pWaitSemaphores = waitSemaphores
        submitInfo.pWaitDstStageMask = waitStages

        cmdBuffers = ffi.new('VkCommandBuffer[]', [self.__commandBuffers[imageIndex], ])
        submitInfo.commandBufferCount = 1
        submitInfo.pCommandBuffers = cmdBuffers

        signalSemaphores = ffi.new('VkSemaphore[]', [self.__renderFinishedSemaphore])
        submitInfo.signalSemaphoreCount = 1
        submitInfo.pSignalSemaphores = signalSemaphores

        vkQueueSubmit(self.__graphicsQueue, 1, submitInfo, VK_NULL_HANDLE)

        swapChains = [self.__swapChain]
        presentInfo = VkPresentInfoKHR(
            sType=VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
            waitSemaphoreCount=1,
            pWaitSemaphores=signalSemaphores,
            swapchainCount=1,
            pSwapchains=swapChains,
            pImageIndices=[imageIndex]
        )

        vkQueuePresentKHR(self.__presentQueue, presentInfo)

    def __createShaderModule(self, shaderFile):
        with open(shaderFile, 'rb') as sf:
            code = sf.read()
            codeSize = len(code)

            createInfo = VkShaderModuleCreateInfo(
                sType=VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
                codeSize=codeSize,
                pCode=code
            )

            return vkCreateShaderModule(self.__device, createInfo, None)


    def __chooseSwapSurfaceFormat(self, availableFormats):
        if len(availableFormats) == 1 and availableFormats[0].format == VK_FORMAT_UNDEFINED:
            return VkSurfaceFormatKHR(VK_FORMAT_B8G8R8A8_UNORM, 0)

        for availableFormat in availableFormats:
            if availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM and availableFormat.colorSpace == 0:
                return availableFormat

        return availableFormats[0]

    def __chooseSwapPresentMode(self, availablePresentModes):
        for availablePresentMode in availablePresentModes:
            if availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR:
                return availablePresentMode

        return VK_PRESENT_MODE_FIFO_KHR

    def __chooseSwapExtent(self, capabilities):
        width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, WIDTH))
        height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, HEIGHT))
        return VkExtent2D(width, height)

    def __querySwapChainSupport(self, device):
        details = SwapChainSupportDetails()

        vkGetPhysicalDeviceSurfaceCapabilitiesKHR = vkGetInstanceProcAddr(self.__instance, 'vkGetPhysicalDeviceSurfaceCapabilitiesKHR')
        details.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface)

        vkGetPhysicalDeviceSurfaceFormatsKHR = vkGetInstanceProcAddr(self.__instance, 'vkGetPhysicalDeviceSurfaceFormatsKHR')
        details.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface)

        vkGetPhysicalDeviceSurfacePresentModesKHR = vkGetInstanceProcAddr(self.__instance, 'vkGetPhysicalDeviceSurfacePresentModesKHR')
        details.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface)

        return details

    def __isDeviceSuitable(self, device):
        indices = self.__findQueueFamilies(device)
        extensionsSupported = self.__checkDeviceExtensionSupport(device)
        swapChainAdequate = False
        if extensionsSupported:
            swapChainSupport = self.__querySwapChainSupport(device)
            swapChainAdequate = (not swapChainSupport.formats is None) and (not swapChainSupport.presentModes is None)
        return indices.isComplete() and extensionsSupported and swapChainAdequate

    def __checkDeviceExtensionSupport(self, device):
        availableExtensions = vkEnumerateDeviceExtensionProperties(device, None)

        for extension in availableExtensions:
            if extension.extensionName in deviceExtensions:
                return True

        return False

    def __findQueueFamilies(self, device):
        vkGetPhysicalDeviceSurfaceSupportKHR = vkGetInstanceProcAddr(self.__instance,
                                                                   'vkGetPhysicalDeviceSurfaceSupportKHR')
        indices = QueueFamilyIndices()

        queueFamilies = vkGetPhysicalDeviceQueueFamilyProperties(device)

        for i, queueFamily in enumerate(queueFamilies):
            if queueFamily.queueCount > 0 and queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT:
                indices.graphicsFamily = i

            presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface)

            if queueFamily.queueCount > 0 and presentSupport:
                indices.presentFamily = i

            if indices.isComplete():
                break

        return indices

    def __getRequiredExtensions(self):
        extensions = list(map(str, glfw.get_required_instance_extensions()))

        if enableValidationLayers:
            extensions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)

        return extensions

    def __checkValidationLayerSupport(self):
        availableLayers = vkEnumerateInstanceLayerProperties()
        for layerName in validationLayers:
            layerFound = False

            for layerProperties in availableLayers:
                if layerName == layerProperties.layerName:
                    layerFound = True
                    break
            if not layerFound:
                return False

        return True

    def run(self):
        self.__initWindow()
        self.__initVulkan()
        self.__mainLoop()


if __name__ == '__main__':

    app = HelloTriangleApplication()

    app.run()

    del app
    glfw.terminate()

Але в мене не працює. Напевно vulkan API не підтримує моя відеокарта. Помилка на 174: "validation layers requested, but not available!" (я особливо не розібрався, просто цікаво було чи працює приклади з github )

12

Re: [Пост] Як створити гру за допомогою python та javascript

Betterthanyou написав:

Але в мене не працює. Напевно vulkan API не підтримує моя відеокарта. Помилка на 174: "validation layers requested, but not available!" (я особливо не розібрався, просто цікаво було чи працює приклади з github)

Перевірити чи підтримує ваша відеокарта Vulkan можна тут. Звісно, це також може означати, що вам потрібно буде обновити драйвери своєї відеокартки.

Хоча мені здається справа зовсім не у цьому. Якщо я не помиляюсь Vulkan вміє сам виявити чи підтримується він. Так, вміє, ви б мали впасти тоді на vkCreateInstance.

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCreateInstance.html написав:

On failure, this command returns

  • VK_ERROR_OUT_OF_HOST_MEMORY

  • VK_ERROR_OUT_OF_DEVICE_MEMORY

  • VK_ERROR_INITIALIZATION_FAILED

  • VK_ERROR_LAYER_NOT_PRESENT

  • VK_ERROR_EXTENSION_NOT_PRESENT

  • VK_ERROR_INCOMPATIBLE_DRIVER

У вас проблема з validation layer'ом, а саме з тим, що:

validationLayers = ["VK_LAYER_LUNARG_standard_validation"]

VK_LAYER_LUNARG_standard_validation meta-layer has been deprecated, and removed in SDK 1.1.126.0

All functionality was moved to VK_LAYER_KHRONOS_validation

Подякували: Betterthanyou, Chemist-i2