diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2d78ad..677e2ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,25 @@ on: pull_request: branches: - master + workflow_dispatch: + inputs: + error_debug: + type: boolean + required: false + default: false + force_debug: + type: boolean + required: false + default: false jobs: test-vulkanalia: name: Test - Vulkanalia runs-on: ubuntu-latest + env: + RUST_LOG: info + RUST_VERSION: 1.64.0 steps: # Checkout - name: Checkout Repository @@ -21,7 +34,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.51.0 + toolchain: ${{ env.RUST_VERSION }} components: clippy, rustfmt # Test - name: Cargo Test @@ -40,10 +53,17 @@ jobs: with: command: build args: --manifest-path vulkanalia/Cargo.toml --no-default-features + # Debug + - name: Debug + uses: mxschmitt/action-tmate@v3 + if: ${{ (failure() && inputs.error_debug) || inputs.force_debug }} format-vulkanalia: name: Format - Vulkanalia runs-on: ubuntu-latest + env: + RUST_LOG: info + RUST_VERSION: 1.64.0 steps: # Checkout - name: Checkout Repository @@ -52,7 +72,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.51.0 + toolchain: ${{ env.RUST_VERSION }} components: clippy, rustfmt # Format - name: Cargo Format @@ -64,6 +84,9 @@ jobs: clippy-vulkanalia: name: Clippy - Vulkanalia runs-on: ubuntu-latest + env: + RUST_LOG: info + RUST_VERSION: 1.64.0 steps: # Checkout - name: Checkout Repository @@ -72,7 +95,7 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: 1.51.0 + toolchain: ${{ env.RUST_VERSION }} components: clippy, rustfmt # Clippy - name: Cargo Clippy @@ -113,3 +136,7 @@ jobs: - name: Check Bindings working-directory: ./generator run: ./gradlew run --args="--directory=.. check" + # Debug + - name: Debug + uses: mxschmitt/action-tmate@v3 + if: ${{ (failure() && inputs.error_debug) || inputs.force_debug }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ef726ad..6dfe7ab 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,16 @@ on: push: branches: - master + workflow_dispatch: + inputs: + error_debug: + type: boolean + required: false + default: false + force_debug: + type: boolean + required: false + default: false jobs: @@ -12,7 +22,7 @@ jobs: runs-on: ubuntu-latest env: RUST_LOG: info - RUST_VERSION: 1.51.0 + RUST_VERSION: 1.64.0 MDBOOK_VERSION: 0.4.21 steps: # Checkout @@ -65,3 +75,7 @@ jobs: BRANCH: gh-pages FOLDER: ./tutorial/book/book CLEAN: true + # Debug + - name: Debug + uses: mxschmitt/action-tmate@v3 + if: ${{ (failure() && inputs.error_debug) || inputs.force_debug }} diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 31f6a3d..52d86b1 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -4,7 +4,15 @@ on: schedule: - cron: "0 21 * * *" workflow_dispatch: - inputs: {} + inputs: + error_debug: + type: boolean + required: false + default: false + force_debug: + type: boolean + required: false + default: false jobs: @@ -30,3 +38,7 @@ jobs: --token=${{ secrets.PERSONAL_ACCESS_TOKEN }} \ update \ --repo=${{ github.repository }}" + # Debug + - name: Debug + uses: mxschmitt/action-tmate@v3 + if: ${{ (failure() && inputs.error_debug) || inputs.force_debug }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c112651..1c05ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ ## [0.23.0] - UNRELEASED ### Changed +- Bumped MSRV to 1.64 - Added `no_std` compability for `vulkanalia` and `vulkanalia-sys` crates - Make all extendable output structs parameters in command wrappers (see [#213](https://github.com/KyleMayes/vulkanalia/issues/213) for details) +### Added +- Added `vulkanalia::prelude::v1_3` module (a prelude module for Vulkan 1.3+) + ### Bindings Updates - [September 22, 2023 Vulkan 1.3.265 spec update](https://github.com/KhronosGroup/Vulkan-Docs/commit/4871ab9e57fb07f98bf016cb10a3088924976e29) - [Add a driver ID for AGXV (Asahi) (#2238)](https://github.com/KhronosGroup/Vulkan-Docs/commit/2b587d9c4adc65429a0063704d1ed6abe79ddb78) diff --git a/README.md b/README.md index 7ce6b95..caecb3e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Crate](https://img.shields.io/crates/v/vulkanalia)](https://crates.io/crates/vulkanalia) [![Documentation](https://docs.rs/vulkanalia/badge.svg)](https://docs.rs/vulkanalia) [![CI](https://img.shields.io/github/actions/workflow/status/KyleMayes/vulkanalia/ci.yml?branch=master)](https://github.com/KyleMayes/vulkanalia/actions?query=workflow%3ACI) -![MSRV](https://img.shields.io/badge/MSRV-1.51.0-blue) +![MSRV](https://img.shields.io/badge/MSRV-1.64.0-blue) Vulkan bindings for Rust. diff --git a/generator/src/main/kotlin/com/kylemayes/generator/Main.kt b/generator/src/main/kotlin/com/kylemayes/generator/Main.kt index 9cec3f7..0311e56 100644 --- a/generator/src/main/kotlin/com/kylemayes/generator/Main.kt +++ b/generator/src/main/kotlin/com/kylemayes/generator/Main.kt @@ -130,7 +130,6 @@ class Update : CliktCommand(help = "Updates generated Vulkan bindings") { override fun run() { val inputs = getRepositoryInputs(context) - inputs.updateLocal(context) if (!force && !inputs.list.any { it.stale }) { log.info { "Nothing to update." } diff --git a/generator/src/main/kotlin/com/kylemayes/generator/support/Command.kt b/generator/src/main/kotlin/com/kylemayes/generator/support/Command.kt index d860c3d..9c30f84 100644 --- a/generator/src/main/kotlin/com/kylemayes/generator/support/Command.kt +++ b/generator/src/main/kotlin/com/kylemayes/generator/support/Command.kt @@ -2,9 +2,14 @@ package com.kylemayes.generator.support +import mu.KotlinLogging +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference +import kotlin.time.Duration.Companion.seconds + +private val log = KotlinLogging.logger { /* */ } /** Executes the `git` command and prints the output. */ fun git(vararg args: String) { @@ -13,53 +18,74 @@ fun git(vararg args: String) { } /** Executes the `rustfmt` command and returns the output. */ -fun rustfmt(rust: String) = execute("rustfmt", emptyArray(), rust) +fun rustfmt(rust: String): String = execute("rustfmt", emptyArray(), rust) -/** Executes a command in a new thread (with a time limit) and returns the output. */ +/** Executes a command (with a time limit) and returns the output. */ private fun execute(command: String, args: Array, input: String? = null): String { val process = ProcessBuilder(command, *args).start() - val output = AtomicReference(null) - val error = AtomicReference(null) - val latch = CountDownLatch(1) + val errors = ConcurrentHashMap() + val latch = CountDownLatch(4) + val stdout = AtomicReference(null) + val stderr = AtomicReference(null) - val thread = Thread { + fun operation(name: String, operation: () -> Unit) = Thread { try { - Thread.currentThread().name = "$command-thread" - - if (input != null) { - process.outputStream.write(input.toByteArray()) - process.outputStream.close() - } + Thread.currentThread().name = "$command-$name" + log.slow("`$command`: $name", 2.5.seconds) { operation() } + } catch (e: Throwable) { + errors[name] = e + } finally { + latch.countDown() + } + } - output.set(String(process.inputStream.readAllBytes())) + val threads = listOf( + operation("write stdin") { + if (input != null) process.outputStream.write(input.toByteArray()) + process.outputStream.flush() + process.outputStream.close() + }, + operation("read stdout") { + stdout.set(String(process.inputStream.readAllBytes())) process.inputStream.close() - + }, + operation("read stderr") { + stderr.set(String(process.errorStream.readAllBytes())) + process.errorStream.close() + }, + operation("wait") { process.waitFor() + }, + ) + + threads.forEach { it.start() } + val countdown = latch.await(5, TimeUnit.SECONDS) - if (process.exitValue() != 0) { - error("Non-zero exit code (${process.exitValue()}).") + val stdoutValue = stdout.get() + val stderrValue = stderr.get() + val errorsValue = errors.toMap() + + if (process.isAlive) { + log.slow("`$command`: kill", 0.5.seconds) { + try { + process.destroy() + process.waitFor(1, TimeUnit.SECONDS) + process.destroyForcibly() + } catch (e: Error) { + e.printStackTrace() } - } catch (e: Throwable) { - error.set(e) - } finally { - latch.countDown() } } - thread.start() - latch.await(5, TimeUnit.SECONDS) - - val outputValue = output.get() - val errorValue = error.get() - if (outputValue == null || errorValue != null) { - process.destroy() - process.waitFor(1, TimeUnit.SECONDS) - process.destroyForcibly() + if (stdoutValue == null || errorsValue.isNotEmpty()) { val executed = "$command ${args.joinToString(" ")}".trim() - val message = "Failed to execute command ('$executed')." - throw RuntimeException(message, errorValue ?: RuntimeException("Timed out.")) + val message = "Failed to execute command ('$executed'):\n\n[stdout]=\n$stdoutValue\n\n[stderr]=\n$stderrValue" + val error = RuntimeException(message) + if (!countdown) error.addSuppressed(RuntimeException("Timed out.")) + errorsValue.forEach { error.addSuppressed(RuntimeException(it.key, it.value)) } + throw error } - return outputValue + return stdoutValue } diff --git a/generator/src/main/kotlin/com/kylemayes/generator/support/Logger.kt b/generator/src/main/kotlin/com/kylemayes/generator/support/Logger.kt index 1ded263..71610a6 100644 --- a/generator/src/main/kotlin/com/kylemayes/generator/support/Logger.kt +++ b/generator/src/main/kotlin/com/kylemayes/generator/support/Logger.kt @@ -3,6 +3,8 @@ package com.kylemayes.generator.support import mu.KLogger +import java.util.concurrent.atomic.AtomicBoolean +import kotlin.time.Duration /** Times a block of code. */ fun KLogger.time(name: String, block: () -> T): T { @@ -13,3 +15,34 @@ fun KLogger.time(name: String, block: () -> T): T { info { "[ END ] $name (${elapsed}s)" } return result } + +/** Times a block of code if it is unexpectedly slow. */ +fun KLogger.slow(name: String, limit: Duration, block: () -> T): T { + val done = AtomicBoolean(false) + val slow = AtomicBoolean(false) + + val thread = Thread { + try { + Thread.sleep(limit.inWholeMilliseconds) + if (!done.get()) { + slow.set(true) + warn { "[UPDATE(SLOW)] $name is taking a while (>${limit}ms)..." } + } + } catch (_: InterruptedException) {} + } + + thread.start() + + val start = System.nanoTime() + val result = block() + + done.set(true) + thread.interrupt() + + if (slow.get()) { + val elapsed = "%.3f".format((System.nanoTime() - start) / 1_000_000_000.0) + warn { "[ END (SLOW) ] $name (${elapsed}s)" } + } + + return result +} diff --git a/tutorial/book/src/development_environment.md b/tutorial/book/src/development_environment.md index 2e4ef79..fbc2d48 100644 --- a/tutorial/book/src/development_environment.md +++ b/tutorial/book/src/development_environment.md @@ -1,6 +1,6 @@ # Development environment -In this chapter we'll set up your environment for developing Vulkan applications by installing the Vulkan SDK for your operating system. This tutorial assumes you already have a working Rust (1.51+) development environment. +In this chapter we'll set up your environment for developing Vulkan applications by installing the Vulkan SDK for your operating system. This tutorial assumes you already have a working Rust (1.64+) development environment. ## Cargo project diff --git a/tutorial/book/src/introduction.md b/tutorial/book/src/introduction.md index cdcd6e0..eb952f2 100644 --- a/tutorial/book/src/introduction.md +++ b/tutorial/book/src/introduction.md @@ -16,7 +16,7 @@ With that out of the way, let's cover some prerequisites for following this tuto * A graphics card and driver compatible with Vulkan ([NVIDIA](https://developer.nvidia.com/vulkan-driver), [AMD](http://www.amd.com/en-us/innovations/software-technologies/technologies-gaming/vulkan), [Intel](https://software.intel.com/en-us/blogs/2016/03/14/new-intel-vulkan-beta-1540204404-graphics-driver-for-windows-78110-1540)) * Experience with Rust -* Rust 1.51 or later +* Rust 1.64 or later * Some existing experience with 3D computer graphics This tutorial will not assume knowledge of OpenGL or Direct3D concepts, but it does require you to know the basics of 3D computer graphics. It will not explain the math behind perspective projection, for example. See [this online book](https://paroj.github.io/gltut/) for a great introduction of computer graphics concepts. Some other great computer graphics resources are: diff --git a/vulkanalia/src/lib.rs b/vulkanalia/src/lib.rs index 3f5bdb8..c0ddffa 100644 --- a/vulkanalia/src/lib.rs +++ b/vulkanalia/src/lib.rs @@ -48,6 +48,12 @@ pub mod prelude { pub use crate::prelude::v1_1::*; pub use crate::vk::{DeviceV1_2, EntryV1_2, InstanceV1_2}; } + + /// Vulkan 1.3 prelude. + pub mod v1_3 { + pub use crate::prelude::v1_2::*; + pub use crate::vk::{DeviceV1_3, EntryV1_3, InstanceV1_3}; + } } /// The result of a executing a fallible Vulkan command.