Compare commits

..

37 Commits

Author SHA1 Message Date
kbz_8 7da8eec788 fixing CI
Test / build_and_test (push) Successful in 22s
Build / build (push) Successful in 31s
2026-06-02 22:21:58 +02:00
kbz_8 e68ee5217d fixing CI
Test / build_and_test (push) Successful in 20s
Build / build (push) Successful in 26s
2026-06-02 22:19:48 +02:00
kbz_8 191db8a8e4 fixing CI
Test / build_and_test (push) Successful in 19s
Build / build (push) Successful in 29s
2026-06-02 22:14:58 +02:00
kbz_8 657bb38810 fixing CI
Test / build_and_test (push) Successful in 22s
Build / build (push) Successful in 31s
2026-06-02 22:12:31 +02:00
kbz_8 8cd40c40f0 fixing CI
Test / build_and_test (push) Successful in 20s
Build / build (push) Successful in 26s
2026-06-02 22:09:41 +02:00
kbz_8 042662a928 fixing CI
Test / build_and_test (push) Successful in 19s
Build / build (push) Successful in 30s
2026-06-02 22:01:34 +02:00
kbz_8 d4e835b6af fixing CI
Test / build_and_test (push) Successful in 23s
Build / build (push) Failing after 35s
2026-06-02 21:54:16 +02:00
kbz_8 a7aad1c2e8 fixing CI
Test / build_and_test (push) Successful in 19s
Build / build (push) Successful in 28s
2026-06-02 21:46:37 +02:00
kbz_8 76be771215 fixing CI
Test / build_and_test (push) Successful in 18s
Build / build (push) Failing after 26s
2026-06-02 21:41:50 +02:00
kbz_8 4b5aaf3d6f try new deployment
Test / build_and_test (push) Successful in 1m21s
Build / build (push) Failing after 1m42s
2026-06-02 21:36:58 +02:00
kbz_8 6dc82d4a68 fixing all image operation tests
Test / build_and_test (push) Successful in 26s
Build / build (push) Successful in 1m3s
2026-06-01 22:13:43 +02:00
kbz_8 19c8d84e05 improving draw line
Test / build_and_test (push) Successful in 30s
Build / build (push) Successful in 1m3s
2026-05-30 13:10:34 +02:00
kbz_8 13d53abbdf improving fragment outputs management
Build / build (push) Successful in 2m15s
Test / build_and_test (push) Successful in 1m31s
2026-05-30 01:45:15 +02:00
kbz_8 0ab6581fe9 new identity
Test / build_and_test (push) Successful in 45s
Build / build (push) Failing after 2h4m30s
2026-05-29 16:42:06 +02:00
kbz_8 ba874a77b2 yes
Test / build_and_test (push) Successful in 38s
Build / build (push) Successful in 53s
2026-05-29 00:45:45 +02:00
kbz_8 b26c4ff9e6 yets
Test / build_and_test (push) Successful in 26s
Build / build (push) Successful in 58s
2026-05-29 00:45:07 +02:00
kbz_8 fc2407d10d adding attachments resolving to renderpass commands
Test / build_and_test (push) Successful in 26s
Build / build (push) Successful in 56s
2026-05-28 01:05:23 +02:00
kbz_8 fe391bc678 adding proper subpass management and image resolve
Test / build_and_test (push) Successful in 29s
Build / build (push) Successful in 1m5s
2026-05-27 23:31:05 +02:00
kbz_8 6edb856d06 fixing sneaky bug in blitter clear
Test / build_and_test (push) Successful in 1m4s
Build / build (push) Successful in 1m42s
2026-05-27 00:47:12 +02:00
kbz_8 ae2bdd03a0 improving image clear
Test / build_and_test (push) Successful in 24s
Build / build (push) Successful in 56s
2026-05-26 01:25:05 +02:00
kbz_8 5a91956939 fixing lots of copy tests issues
Test / build_and_test (push) Successful in 33s
Build / build (push) Successful in 1m0s
2026-05-24 22:04:10 +02:00
kbz_8 bd2774b7f9 fixing image layers and mipmaps
Test / build_and_test (push) Successful in 26s
Build / build (push) Successful in 59s
2026-05-24 13:22:16 +02:00
kbz_8 fe6b0b3b23 fixing all format blits, adding srgb convertion
Test / build_and_test (push) Successful in 33s
Build / build (push) Successful in 1m13s
2026-05-23 23:19:22 +02:00
kbz_8 afc47bf29f improving blitter formats management
Test / build_and_test (push) Successful in 30s
Build / build (push) Successful in 2m26s
2026-05-23 01:06:25 +02:00
kbz_8 d4d0f70472 updating spirv interpreter
Test / build_and_test (push) Successful in 36s
Build / build (push) Successful in 1m5s
2026-05-22 10:44:35 +02:00
kbz_8 bb482465fe adding formats management
Test / build_and_test (push) Successful in 41s
Build / build (push) Successful in 1m7s
2026-05-21 18:52:43 +02:00
kbz_8 800c867cdd base texture sampling
Test / build_and_test (push) Successful in 46s
Build / build (push) Successful in 1m15s
2026-05-20 18:30:36 +02:00
kbz_8 4d344e83d3 fixing tests
Build / build (push) Successful in 1m25s
Test / build_and_test (push) Successful in 32s
2026-05-18 23:52:59 +02:00
kbz_8 9e4221affb improving descriptor sets push to shaders
Test / build_and_test (push) Successful in 35s
Build / build (push) Successful in 1m16s
2026-05-18 19:54:09 +02:00
kbz_8 3c9a864240 fixing crashes
Test / build_and_test (push) Successful in 49s
Build / build (push) Successful in 1m28s
2026-05-17 01:58:20 +02:00
kbz_8 134925a16e adding descriptor sets injections to graphics stages
Test / build_and_test (push) Successful in 45s
Build / build (push) Successful in 1m18s
2026-05-15 00:58:58 +02:00
kbz_8 d460f22a45 improving rasterization performances
Test / build_and_test (push) Successful in 33s
Build / build (push) Successful in 1m2s
2026-05-14 21:44:53 +02:00
kbz_8 1eb367ac17 adding drawcall stats
Build / build (push) Successful in 58s
Test / build_and_test (push) Successful in 33s
2026-05-14 14:27:27 +02:00
kbz_8 c2dad9fe6d adding base depth buffers
Test / build_and_test (push) Successful in 28s
Build / build (push) Successful in 1m8s
2026-05-14 01:35:26 +02:00
kbz_8 124ea12d2e fixing slow memory leak
Test / build_and_test (push) Successful in 28s
Build / build (push) Successful in 1m4s
2026-05-14 00:23:46 +02:00
kbz_8 b5b05776d8 refactoring renderer
Test / build_and_test (push) Successful in 35s
Build / build (push) Successful in 1m20s
2026-05-13 22:05:25 +02:00
kbz_8 faae8e86e0 implementing push constants 2026-05-12 03:01:17 +02:00
53 changed files with 4151 additions and 1478 deletions
+22 -21
View File
@@ -6,32 +6,33 @@ on:
pull_request: pull_request:
branches: [ "master" ] branches: [ "master" ]
permissions:
contents: read
deployments: write
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'ci skip')" if: ${{ github.event_name != 'push' || !contains(github.event.head_commit.message, 'ci skip') }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: https://codeberg.org/mlugg/setup-zig@v2 with:
- uses: actions/setup-node@v6 fetch-depth: 0
with:
node-version: 24
- name: Building - uses: https://codeberg.org/mlugg/setup-zig@v2
run: zig build soft
- name: Generating docs - name: Building
run: zig build docs run: zig build soft
- name: Publish to Cloudflare Pages - name: Generating docs
uses: cloudflare/wrangler-action@v3 run: zig build docs
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} - name: Deploying docs
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
command: pages deploy zig-out/docs --project-name=vulkan-driver-docs uses: milanmk/actions-file-deployer@master
gitHubToken: ${{ secrets.GITHUB_TOKEN }} with:
remote-protocol: sftp
remote-host: ${{ secrets.SFTP_HOST_DOCS }}
remote-user: ${{ secrets.SFTP_USER_DOCS }}
remote-password: ${{ secrets.SFTP_PASSWORD_DOCS }}
remote-port: 6969
local-path: "./zig-out/docs"
remote-path: "/www"
sync: full
-77
View File
@@ -1,77 +0,0 @@
name: CTS
on:
workflow_dispatch:
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: https://codeberg.org/mlugg/setup-zig@v2
- uses: actions/setup-node@v6
with:
node-version: 24
- name: Install system dependencies
run: |
apt update
apt install -y libgl1 libwayland-egl1 libwayland-cursor0 clang libwayland-server0
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: x86_64-unknown-linux-gnu,wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2
with:
cache-all-crates: "true"
cache-on-failure: "false"
- name: Install cargo tools
uses: cargo-bins/cargo-binstall@main
- name: Install Dioxus
shell: bash
run: |
source $HOME/.cargo/env
cargo binstall dioxus-cli --no-confirm --force --version 0.7.9
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install deqp-runner
run: cargo install deqp-runner
- name: Verify installations
shell: bash
run: |
source $HOME/.cargo/env
echo "Verifying tool installations..."
which dx && dx --version || echo "dioxus-cli not found"
which deqp-runner && deqp-runner --version || echo "deqp-runner not found"
- name: Run Vulkan CTS
run: zig build cts-soft --release=fast -- -j4
continue-on-error: true
- name: Verify tests
run: ls cts | grep "results.csv";
- name: Cloning CTS viewer
run: git clone https://git.kbz8.me/kbz_8/VulkanCTSViewer.git
- name: Build CTS viewer
env:
URL: https://vulkan-driver-cts-report.kbz8.me/
run: |
cd VulkanCTSViewer
zip ./assets/results.zip ../cts/results.csv
dx build --verbose --platform web --release
- name: Publish to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy VulkanCTSViewer/target/dx/vulkan-cts-analyzer/release/web/public --project-name=vulkan-driver-cts-report
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
+64
View File
@@ -0,0 +1,64 @@
name: CTS Soft
on:
workflow_dispatch:
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: https://codeberg.org/mlugg/setup-zig@v2
- name: Install system dependencies
run: |
apt update
apt install -y libgl1 libwayland-egl1 libwayland-cursor0 clang libwayland-server0
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: x86_64-unknown-linux-gnu
- uses: Swatinem/rust-cache@v2
with:
cache-all-crates: "true"
cache-on-failure: "false"
- name: Install deqp-runner
run: cargo install deqp-runner
- name: Verify installations
shell: bash
run: |
source $HOME/.cargo/env
echo "Verifying tool installations..."
which deqp-runner && deqp-runner --version || echo "deqp-runner not found"
- name: Run Vulkan CTS
run: zig build cts-soft --release=fast -- -j4
continue-on-error: true
- name: Verify tests
run: ls cts | grep "results.csv";
- name: Archiving results
run: |
mkdir -p assets/
zip ./assets/results.zip ./cts/results.csv
- name: Deploying CTS results
uses: milanmk/actions-file-deployer@master
with:
remote-protocol: sftp
remote-host: ${{ secrets.SFTP_HOST_CTS_SOFT }}
remote-user: ${{ secrets.SFTP_USER_CTS_SOFT }}
remote-password: ${{ secrets.SFTP_PASSWORD_CTS_SOFT }}
remote-port: 6969
local-path: "./assets"
remote-path: "/www/assets"
sync: full
+1
View File
@@ -9,6 +9,7 @@ scripts/__pycache__/
.gdb_history .gdb_history
*.json *.json
*.png *.png
!logo.png
*.bin *.bin
*.qpa *.qpa
*.xml *.xml
+23 -20
View File
@@ -1,10 +1,10 @@
# Stroll Vulkan ICD <a href="https://git.kbz8.me/kbz_8/VulkanDriver/actions?workflows=Build.yml"><img src="https://git.kbz8.me/kbz_8/VulkanDriver/actions/workflows/Build.yml/badge.svg"></a> <a href="https://git.kbz8.me/kbz_8/VulkanDriver/actions?workflows=Test.yml"><img src="https://git.kbz8.me/kbz_8/VulkanDriver/actions/workflows/Test.yml/badge.svg"></a> # Ape Vulkan ICD <a href="https://git.kbz8.me/kbz_8/VulkanDriver/actions?workflows=Build.yml"><img src="https://git.kbz8.me/kbz_8/VulkanDriver/actions/workflows/Build.yml/badge.svg"></a> <a href="https://git.kbz8.me/kbz_8/VulkanDriver/actions?workflows=Test.yml"><img src="https://git.kbz8.me/kbz_8/VulkanDriver/actions/workflows/Test.yml/badge.svg"></a>
<img align="right" src="https://matthew.kerwin.net.au/blog_files/kappa"/> <img align="right" width="250px" src="./logo.png"/>
A driver as slow as Lance Stroll. For I feel as an ape, smiting sticks together in the vain hope of forging a driver.
Here lies the source code of a rather calamitous attempt at the Vulkan specification, shaped into an Installable Client Driver for a software-based renderer, all written in Zig. Here lies the source code of a rather calamitous attempt at the Vulkan specification, shaped into an Installable Client Driver all written in Zig.
It was forged for my own learning and amusement alone. Pray, do not wield it in any earnest project, lest thy hopes and frame rates both find themselves entombed. It was forged for my own learning and amusement alone. Pray, do not wield it in any earnest project, lest thy hopes and frame rates both find themselves entombed.
@@ -14,11 +14,16 @@ To understand Vulkan - not as a humble API mere mortals call upon, but as a laby
It does not seek to produce a performant or production-worthy driver. \ It does not seek to produce a performant or production-worthy driver. \
*The gods are merciful, but not that merciful.* *The gods are merciful, but not that merciful.*
## Build ## Soft [software implementation]
Soft be a software implementation of the Vulkan specification, abiding within this driver's own codebase.\
It maketh use of a bespoke [SPIR-V interpreter](https://git.kbz8.me/kbz_8/SPIRV-Interpreter) and renderer, by whose workings its labours are carried forth.
### Build
If thou art truly determined: If thou art truly determined:
``` ```
zig build zig build soft
``` ```
Then ensure thy Vulkan loader is pointed toward the ICD manifest. Then ensure thy Vulkan loader is pointed toward the ICD manifest.
@@ -26,8 +31,7 @@ The precise ritual varies by system - consult the tomes of your operating system
Use at your own risk. If thy machine shudders, weeps, or attempts to flee - know that it was warned. Use at your own risk. If thy machine shudders, weeps, or attempts to flee - know that it was warned.
## Vulkan 1.0 specification #### Vulkan 1.0 specification
<details> <details>
<summary> <summary>
The present standing of thy Vulkan 1.0 specification's implementation The present standing of thy Vulkan 1.0 specification's implementation
@@ -55,7 +59,7 @@ vkCmdBindVertexBuffers | ✅ Implemented
vkCmdBlitImage | ✅ Implemented vkCmdBlitImage | ✅ Implemented
vkCmdClearAttachments | ✅ Implemented vkCmdClearAttachments | ✅ Implemented
vkCmdClearColorImage | ✅ Implemented vkCmdClearColorImage | ✅ Implemented
vkCmdClearDepthStencilImage | ⚙️ WIP vkCmdClearDepthStencilImage | ✅ Implemented
vkCmdCopyBuffer | ✅ Implemented vkCmdCopyBuffer | ✅ Implemented
vkCmdCopyBufferToImage | ✅ Implemented vkCmdCopyBufferToImage | ✅ Implemented
vkCmdCopyImage | ✅ Implemented vkCmdCopyImage | ✅ Implemented
@@ -71,18 +75,18 @@ vkCmdEndQuery | ⚙️ WIP
vkCmdEndRenderPass | ✅ Implemented vkCmdEndRenderPass | ✅ Implemented
vkCmdExecuteCommands | ✅ Implemented vkCmdExecuteCommands | ✅ Implemented
vkCmdFillBuffer | ✅ Implemented vkCmdFillBuffer | ✅ Implemented
vkCmdNextSubpass | ⚙️ WIP vkCmdNextSubpass | ✅ Implemented
vkCmdPipelineBarrier | ✅ Implemented vkCmdPipelineBarrier | ✅ Implemented
vkCmdPushConstants | ⚙️ WIP vkCmdPushConstants | ✅ Implemented
vkCmdResetEvent | ✅ Implemented vkCmdResetEvent | ✅ Implemented
vkCmdResetQueryPool | ⚙️ WIP vkCmdResetQueryPool | ⚙️ WIP
vkCmdResolveImage | ⚙️ WIP vkCmdResolveImage | ✅ Implemented
vkCmdSetBlendConstants | ⚙️ WIP vkCmdSetBlendConstants | ⚙️ WIP
vkCmdSetDepthBias | ⚙️ WIP vkCmdSetDepthBias | ⚙️ WIP
vkCmdSetDepthBounds | ⚙️ WIP vkCmdSetDepthBounds | ⚙️ WIP
vkCmdSetEvent | ✅ Implemented vkCmdSetEvent | ✅ Implemented
vkCmdSetLineWidth | ⚙️ WIP vkCmdSetLineWidth | ⚙️ WIP
vkCmdSetScissor | ⚙️ WIP vkCmdSetScissor | ✅ Implemented
vkCmdSetStencilCompareMask | ⚙️ WIP vkCmdSetStencilCompareMask | ⚙️ WIP
vkCmdSetStencilReference | ⚙️ WIP vkCmdSetStencilReference | ⚙️ WIP
vkCmdSetStencilWriteMask | ⚙️ WIP vkCmdSetStencilWriteMask | ⚙️ WIP
@@ -91,7 +95,7 @@ vkCmdUpdateBuffer | ⚙️ WIP
vkCmdWaitEvents | ✅ Implemented vkCmdWaitEvents | ✅ Implemented
vkCmdWriteTimestamp | ⚙️ WIP vkCmdWriteTimestamp | ⚙️ WIP
vkCreateBuffer | ✅ Implemented vkCreateBuffer | ✅ Implemented
vkCreateBufferView | ⚙️ WIP vkCreateBufferView | ✅ Implemented
vkCreateCommandPool | ✅ Implemented vkCreateCommandPool | ✅ Implemented
vkCreateComputePipelines | ✅ Implemented vkCreateComputePipelines | ✅ Implemented
vkCreateDescriptorPool | ✅ Implemented vkCreateDescriptorPool | ✅ Implemented
@@ -108,17 +112,16 @@ vkCreatePipelineCache | ⚙️ WIP
vkCreatePipelineLayout | ✅ Implemented vkCreatePipelineLayout | ✅ Implemented
vkCreateQueryPool | ⚙️ WIP vkCreateQueryPool | ⚙️ WIP
vkCreateRenderPass | ✅ Implemented vkCreateRenderPass | ✅ Implemented
vkCreateSampler | ⚙️ WIP vkCreateSampler | ✅ Implemented
vkCreateSemaphore | ⚙️ WIP vkCreateSemaphore | ⚙️ WIP
vkCreateShaderModule | ✅ Implemented vkCreateShaderModule | ✅ Implemented
vkCreateSwapchainKHR | ⚙️ WIP
vkCreateSwapchainKHR | ✅ Implemented vkCreateSwapchainKHR | ✅ Implemented
vkCreateWaylandSurfaceKHR | ✅ Implemented vkCreateWaylandSurfaceKHR | ✅ Implemented
vkCreateWin32SurfaceKHR | ⚙️ WIP vkCreateWin32SurfaceKHR | ⚙️ WIP
vkCreateXcbSurfaceKHR | ⚙️ WIP vkCreateXcbSurfaceKHR | ⚙️ WIP
vkCreateXlibSurfaceKHR | ⚙️ WIP vkCreateXlibSurfaceKHR | ⚙️ WIP
vkDestroyBuffer | ✅ Implemented vkDestroyBuffer | ✅ Implemented
vkDestroyBufferView | ⚙️ WIP vkDestroyBufferView | ✅ Implemented
vkDestroyCommandPool | ✅ Implemented vkDestroyCommandPool | ✅ Implemented
vkDestroyDescriptorPool | ✅ Implemented vkDestroyDescriptorPool | ✅ Implemented
vkDestroyDescriptorSetLayout | ✅ Implemented vkDestroyDescriptorSetLayout | ✅ Implemented
@@ -157,7 +160,7 @@ vkGetDeviceQueue | ✅ Implemented
vkGetEventStatus | ✅ Implemented vkGetEventStatus | ✅ Implemented
vkGetFenceStatus | ✅ Implemented vkGetFenceStatus | ✅ Implemented
vkGetImageMemoryRequirements | ✅ Implemented vkGetImageMemoryRequirements | ✅ Implemented
vkGetImageSparseMemoryRequirements | ⚙️ WIP vkGetImageSparseMemoryRequirements | ❎ Unsupported
vkGetImageSubresourceLayout | ✅ Implemented vkGetImageSubresourceLayout | ✅ Implemented
vkGetInstanceProcAddr | ✅ Implemented vkGetInstanceProcAddr | ✅ Implemented
vkGetPhysicalDeviceFeatures | ✅ Implemented vkGetPhysicalDeviceFeatures | ✅ Implemented
@@ -166,7 +169,7 @@ vkGetPhysicalDeviceImageFormatProperties | ✅ Implemented
vkGetPhysicalDeviceMemoryProperties | ✅ Implemented vkGetPhysicalDeviceMemoryProperties | ✅ Implemented
vkGetPhysicalDeviceProperties | ✅ Implemented vkGetPhysicalDeviceProperties | ✅ Implemented
vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented vkGetPhysicalDeviceQueueFamilyProperties | ✅ Implemented
vkGetPhysicalDeviceSparseImageFormatProperties | ⚙️ WIP vkGetPhysicalDeviceSparseImageFormatProperties | ❎ Unsupported
vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented vkGetPhysicalDeviceSurfaceCapabilitiesKHR | ✅ Implemented
vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented vkGetPhysicalDeviceSurfaceFormatsKHR | ✅ Implemented
vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented vkGetPhysicalDeviceSurfacePresentModesKHR | ✅ Implemented
@@ -182,7 +185,7 @@ vkGetSwapchainImagesKHR | ✅ Implemented
vkInvalidateMappedMemoryRanges | ✅ Implemented vkInvalidateMappedMemoryRanges | ✅ Implemented
vkMapMemory | ✅ Implemented vkMapMemory | ✅ Implemented
vkMergePipelineCaches | ⚙️ WIP vkMergePipelineCaches | ⚙️ WIP
vkQueueBindSparse | ⚙️ WIP vkQueueBindSparse | ❎ Unsupported
vkQueuePresentKHR | ✅ Implemented vkQueuePresentKHR | ✅ Implemented
vkQueueSubmit | ✅ Implemented vkQueueSubmit | ✅ Implemented
vkQueueWaitIdle | ✅ Implemented vkQueueWaitIdle | ✅ Implemented
+5 -17
View File
@@ -108,7 +108,7 @@ pub fn build(b: *std.Build) !void {
} }
const icd_file = b.addWriteFile( const icd_file = b.addWriteFile(
b.getInstallPath(.lib, b.fmt("vk_stroll_{s}.json", .{impl.name})), b.getInstallPath(.lib, b.fmt("vk_ape_{s}.json", .{impl.name})),
b.fmt( b.fmt(
\\{{ \\{{
\\ "file_format_version": "1.0.1", \\ "file_format_version": "1.0.1",
@@ -176,14 +176,11 @@ pub fn build(b: *std.Build) !void {
fn customSoft( fn customSoft(
b: *std.Build, b: *std.Build,
lib: *Step.Compile, lib: *Step.Compile,
target: std.Build.ResolvedTarget, _: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode, _: std.builtin.OptimizeMode,
options: *Step.Options, options: *Step.Options,
use_llvm: bool, use_llvm: bool,
) !void { ) !void {
const cpuinfo = b.lazyDependency("cpuinfo", .{}) orelse return error.UnresolvedDependency;
lib.root_module.linkLibrary(cpuinfo.artifact("cpuinfo"));
const spv = b.lazyDependency("SPIRV_Interpreter", .{ const spv = b.lazyDependency("SPIRV_Interpreter", .{
.@"no-example" = true, .@"no-example" = true,
.@"no-test" = true, .@"no-test" = true,
@@ -191,23 +188,13 @@ fn customSoft(
}) orelse return error.UnresolvedDependency; }) orelse return error.UnresolvedDependency;
lib.root_module.addImport("spv", spv.module("spv")); lib.root_module.addImport("spv", spv.module("spv"));
const c_includes = b.addTranslateC(.{
.root_source_file = b.path("src/soft/c_includes.h"),
.target = target,
.optimize = optimize,
.link_libc = false,
});
c_includes.addIncludePath(cpuinfo.path("include"));
lib.root_module.addImport("soft_c", c_includes.createModule());
const single_threaded_option = b.option(bool, "single-threaded", "Single threaded runtime mode") orelse false; const single_threaded_option = b.option(bool, "single-threaded", "Single threaded runtime mode") orelse false;
const debug_allocator_option = b.option(bool, "debug-allocator", "Debug device allocator") orelse false; const debug_allocator_option = b.option(bool, "debug-allocator", "Debug device allocator") orelse false;
const shaders_simd_option = b.option(bool, "shader-simd", "Shaders SIMD acceleration") orelse true; const shaders_simd_option = b.option(bool, "shader-simd", "Shaders SIMD acceleration") orelse true;
const single_threaded_compute_option = b.option(bool, "single-threaded-compute", "Single threaded compute shaders execution") orelse true; const single_threaded_compute_option = b.option(bool, "single-threaded-compute", "Single threaded compute shaders execution") orelse true;
const compute_dump_early_results_table_option = b.option(u32, "compute-dump-early-results-table", "Dump compute shaders results table before invocation"); const compute_dump_early_results_table_option = b.option(u32, "compute-dump-early-results-table", "Dump compute shaders results table before invocation");
const compute_dump_final_results_table_option = b.option(u32, "compute-dump-final-results-table", "Dump compute shaders results table after invocation"); const compute_dump_final_results_table_option = b.option(u32, "compute-dump-final-results-table", "Dump compute shaders results table after invocation");
const approxiamte_rgb_option = b.option(bool, "approximates-rgb", "Approximate sRGB <-> RGB conversions") orelse true;
options.addOption(bool, "single_threaded", single_threaded_option); options.addOption(bool, "single_threaded", single_threaded_option);
options.addOption(bool, "debug_allocator", debug_allocator_option); options.addOption(bool, "debug_allocator", debug_allocator_option);
@@ -215,6 +202,7 @@ fn customSoft(
options.addOption(bool, "single_threaded_compute", single_threaded_compute_option); options.addOption(bool, "single_threaded_compute", single_threaded_compute_option);
options.addOption(?u32, "compute_dump_early_results_table", compute_dump_early_results_table_option); options.addOption(?u32, "compute_dump_early_results_table", compute_dump_early_results_table_option);
options.addOption(?u32, "compute_dump_final_results_table", compute_dump_final_results_table_option); options.addOption(?u32, "compute_dump_final_results_table", compute_dump_final_results_table_option);
options.addOption(bool, "approximates_rgb", approxiamte_rgb_option);
} }
fn addCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile, comptime mode: RunningMode) !*Step { fn addCTS(b: *std.Build, target: std.Build.ResolvedTarget, impl: *const ImplementationDesc, impl_lib: *Step.Compile, comptime mode: RunningMode) !*Step {
+2 -7
View File
@@ -25,14 +25,9 @@
.url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7", .url = "git+https://git.kbz8.me/kbz_8/Vulkan-CTS-bin.git#a5f787d80f14f136e3cb3e1185c35e298846c1d7",
.hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3", .hash = "N-V-__8AAMpOQxkHCKTw9i-NwmmQ3ks1ndFDXcVLlic4KjK3",
}, },
.cpuinfo = .{
.url = "git+https://github.com/Kbz-8/cpuinfo.git#c9bea4f6c166a495ee0ce117821f9627d4aed118",
.hash = "cpuinfo-0.0.1-RLgIQYrTMgGqfQMOd1nAa2EuglXOh5gR9bNzwMzQTemt",
.lazy = true,
},
.SPIRV_Interpreter = .{ .SPIRV_Interpreter = .{
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#9d20363ae852e1b400cb62508cb672bcfd5b3716", .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#9c355fe126d0142ac6d1fae48633993864c90d61",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn6QuBQDDfo3uv6HauRc4DLNp2b0pZkfwyuzF-w9d", .hash = "SPIRV_Interpreter-0.0.1-ajmpn867BQA-J2PA4fGQGnb3WcNtqd_3_W7-goaEocC5",
.lazy = true, .lazy = true,
}, },
//.SPIRV_Interpreter = .{ //.SPIRV_Interpreter = .{
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

+4 -7
View File
@@ -13,9 +13,6 @@ pub const Interface = base.Buffer;
interface: Interface, interface: Interface,
pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.BufferCreateInfo) VkError!*Self { pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const vk.BufferCreateInfo) VkError!*Self {
if (info.size > lib.MAX_MEMORY_ALLOCATION_SIZE)
return VkError.OutOfDeviceMemory;
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@@ -63,7 +60,7 @@ pub fn fillBuffer(self: *Self, offset: vk.DeviceSize, size: vk.DeviceSize, data:
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
var bytes = if (size == vk.WHOLE_SIZE) memory.size - offset else size; var bytes = if (size == vk.WHOLE_SIZE) memory.size - offset else size;
const map = try self.mapAsSliceWithOffset(u32, offset, bytes); const map = try self.mapAsSliceWithOffset(u32, offset, @divFloor(bytes, @sizeOf(u32)) * @sizeOf(u32));
var i: usize = 0; var i: usize = 0;
while (bytes >= 4) : ({ while (bytes >= 4) : ({
@@ -100,18 +97,18 @@ pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, off
pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; const map = try memory.map(offset, @sizeOf(T));
return @alignCast(std.mem.bytesAsValue(T, map)); return @alignCast(std.mem.bytesAsValue(T, map));
} }
pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; const map = try memory.map(offset, @sizeOf(T));
return std.mem.bytesToValue(T, map); return std.mem.bytesToValue(T, map);
} }
pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, size))))[0..size]; const map = try memory.map(offset, size);
return @alignCast(std.mem.bytesAsSlice(T, map)); return @alignCast(std.mem.bytesAsSlice(T, map));
} }
+182 -29
View File
@@ -53,6 +53,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.blitImage = blitImage, .blitImage = blitImage,
.clearAttachment = clearAttachment, .clearAttachment = clearAttachment,
.clearColorImage = clearColorImage, .clearColorImage = clearColorImage,
.clearDepthStencilImage = clearDepthStencilImage,
.copyBuffer = copyBuffer, .copyBuffer = copyBuffer,
.copyBufferToImage = copyBufferToImage, .copyBufferToImage = copyBufferToImage,
.copyImage = copyImage, .copyImage = copyImage,
@@ -67,10 +68,14 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.endRenderPass = endRenderPass, .endRenderPass = endRenderPass,
.executeCommands = executeCommands, .executeCommands = executeCommands,
.fillBuffer = fillBuffer, .fillBuffer = fillBuffer,
.nextSubpass = nextSubpass,
.pipelineBarrier = pipelineBarrier, .pipelineBarrier = pipelineBarrier,
.pushConstants = pushConstants,
.reset = reset, .reset = reset,
.resetEvent = resetEvent, .resetEvent = resetEvent,
.resolveImage = resolveImage,
.setEvent = setEvent, .setEvent = setEvent,
.setScissor = setScissor,
.setViewport = setViewport, .setViewport = setViewport,
.waitEvent = waitEvent, .waitEvent = waitEvent,
}; };
@@ -97,8 +102,10 @@ pub fn execute(self: *Self, device: *ExecutionDevice) void {
for (self.commands.items) |command| { for (self.commands.items) |command| {
command.vtable.execute(@ptrCast(command.ptr), device) catch |err| { command.vtable.execute(@ptrCast(command.ptr), device) catch |err| {
base.errors.errorLoggerContext(err, "the software execution device"); base.errors.errorLoggerContext(err, "the software execution device");
if (@errorReturnTrace()) |trace| { if (comptime base.config.logs == .verbose) {
std.debug.dumpErrorReturnTrace(trace); if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
} }
return; // Should we return or continue ? Maybe device lost ? return; // Should we return or continue ? Maybe device lost ?
}; };
@@ -139,6 +146,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
const impl: *Impl = @ptrCast(@alignCast(context)); const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.render_pass = impl.render_pass; device.renderer.render_pass = impl.render_pass;
device.renderer.framebuffer = impl.framebuffer; device.renderer.framebuffer = impl.framebuffer;
device.renderer.subpass_index = 0;
for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| { for (impl.render_pass.interface.attachments, impl.framebuffer.interface.attachments, 0..) |desc, attachment, index| {
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image)); const image: *SoftImage = @alignCast(@fieldParentPtr("interface", attachment.image));
@@ -150,7 +158,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
} }
switch (desc.stencil_load_op) { switch (desc.stencil_load_op) {
.clear => clear_mask = .{ .stencil_bit = true }, .clear => clear_mask.stencil_bit = true,
else => {}, else => {},
} }
@@ -160,7 +168,7 @@ pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, fra
if (clear_mask.color_bit) { if (clear_mask.color_bit) {
try blitter.clear( try blitter.clear(
(impl.clear_values orelse return VkError.Unknown)[index], (impl.clear_values orelse return VkError.Unknown)[index],
try image.getClearFormat(), try SoftImage.getClearFormatFor(attachment.format),
image, image,
attachment.format, attachment.format,
attachment.subresource_range, attachment.subresource_range,
@@ -373,20 +381,44 @@ pub fn clearAttachment(interface: *Interface, attachment: vk.ClearAttachment, re
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void { pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context)); const impl: *Impl = @ptrCast(@alignCast(context));
if (device.renderer.framebuffer) |framebuffer| { const framebuffer = device.renderer.framebuffer orelse return;
const image_view = framebuffer.interface.attachments[impl.attachment.color_attachment]; const render_pass = device.renderer.render_pass orelse return;
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image)); const subpass = render_pass.interface.subpasses[device.renderer.subpass_index];
const clear_format = try image.getClearFormat();
try blitter.clear( const image_view = blk: {
impl.attachment.clear_value, if (impl.attachment.aspect_mask.toInt() == (vk.ImageAspectFlags{ .color_bit = true }).toInt()) {
clear_format, const fb_attachment_index = (subpass.color_attachments orelse return)[impl.attachment.color_attachment].attachment;
image,
image_view.format, if (fb_attachment_index != vk.ATTACHMENT_UNUSED)
image_view.subresource_range, break :blk framebuffer.interface.attachments[impl.attachment.color_attachment];
null, } else if (impl.attachment.aspect_mask.depth_bit or impl.attachment.aspect_mask.stencil_bit) {
); if (render_pass.interface.subpasses[device.renderer.subpass_index].depth_stencil_attachments) |desc| {
} if (desc.attachment != vk.ATTACHMENT_UNUSED)
break :blk framebuffer.interface.attachments[desc.attachment];
}
}
return;
};
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image));
const clear_format = try SoftImage.getClearFormatFor(image_view.format);
const range: vk.ImageSubresourceRange = .{
.aspect_mask = impl.attachment.aspect_mask,
.base_mip_level = image_view.subresource_range.base_mip_level,
.level_count = image_view.subresource_range.level_count,
.base_array_layer = impl.rect.base_array_layer + image_view.subresource_range.base_array_layer,
.layer_count = impl.rect.layer_count,
};
try blitter.clear(
impl.attachment.clear_value,
clear_format,
image,
image_view.format,
range,
impl.rect.rect,
);
} }
}; };
@@ -427,6 +459,34 @@ pub fn clearColorImage(interface: *Interface, image: *base.Image, _: vk.ImageLay
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
} }
pub fn clearDepthStencilImage(interface: *Interface, image: *base.Image, _: vk.ImageLayout, value: *const vk.ClearDepthStencilValue, range: vk.ImageSubresourceRange) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
image: *SoftImage,
value: vk.ClearDepthStencilValue,
range: vk.ImageSubresourceRange,
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
const clear_format = try impl.image.getClearFormat();
try blitter.clear(.{ .depth_stencil = impl.value }, clear_format, impl.image, impl.image.interface.format, impl.range, null);
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.image = @alignCast(@fieldParentPtr("interface", image)),
.value = value.*,
.range = range,
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void { pub fn copyBuffer(interface: *Interface, src: *base.Buffer, dst: *base.Buffer, regions: []const vk.BufferCopy) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
@@ -728,6 +788,11 @@ pub fn endRenderPass(interface: *Interface) VkError!void {
const CommandImpl = struct { const CommandImpl = struct {
pub fn execute(_: *anyopaque, device: *ExecutionDevice) VkError!void { pub fn execute(_: *anyopaque, device: *ExecutionDevice) VkError!void {
const framebuffer = device.renderer.framebuffer orelse return;
const render_pass = device.renderer.render_pass orelse return;
try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index);
device.renderer.render_pass = null; device.renderer.render_pass = null;
device.renderer.framebuffer = null; device.renderer.framebuffer = null;
} }
@@ -788,15 +853,20 @@ pub fn fillBuffer(interface: *Interface, buffer: *base.Buffer, offset: vk.Device
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
} }
pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags, dst_stage: vk.PipelineStageFlags, dependency: vk.DependencyFlags, memory_barriers: []const vk.MemoryBarrier, buffer_barriers: []const vk.BufferMemoryBarrier, image_barriers: []const vk.ImageMemoryBarrier) VkError!void { pub fn nextSubpass(interface: *Interface, _: vk.SubpassContents) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
const CommandImpl = struct { const CommandImpl = struct {
const Impl = @This(); const Impl = @This();
pub fn execute(_: *anyopaque, _: *ExecutionDevice) VkError!void { pub fn execute(_: *anyopaque, device: *ExecutionDevice) VkError!void {
// TODO: implement synchronization for rasterization stages const framebuffer = device.renderer.framebuffer orelse return;
const render_pass = device.renderer.render_pass orelse return;
try framebuffer.resolveAttachments(render_pass, device.renderer.subpass_index);
device.renderer.subpass_index += 1;
} }
}; };
@@ -804,20 +874,51 @@ pub fn pipelineBarrier(interface: *Interface, src_stage: vk.PipelineStageFlags,
errdefer allocator.destroy(cmd); errdefer allocator.destroy(cmd);
cmd.* = .{}; cmd.* = .{};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
_ = src_stage;
_ = dst_stage;
_ = dependency;
_ = memory_barriers;
_ = buffer_barriers;
_ = image_barriers;
} }
pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void { pub fn pipelineBarrier(_: *Interface, _: vk.PipelineStageFlags, _: vk.PipelineStageFlags, _: vk.DependencyFlags, _: []const vk.MemoryBarrier, _: []const vk.BufferMemoryBarrier, _: []const vk.ImageMemoryBarrier) VkError!void {
// No-op
}
pub fn pushConstants(interface: *Interface, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
_ = stage; const CommandImpl = struct {
const Impl = @This();
stages: vk.ShaderStageFlags,
offset: u32,
blob: []const u8,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
const state = &device.pipeline_states[
if (impl.stages.vertex_bit or impl.stages.fragment_bit)
ExecutionDevice.GRAPHICS_PIPELINE_STATE
else
ExecutionDevice.COMPUTE_PIPELINE_STATE
];
const size = @min(lib.PUSH_CONSTANT_SIZE - impl.offset, impl.blob.len);
@memcpy(state.push_constant_blob[impl.offset .. impl.offset + size], impl.blob[0..size]);
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.stages = stages,
.offset = offset,
.blob = allocator.dupe(u8, blob) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn resetEvent(interface: *Interface, event: *base.Event, _: vk.PipelineStageFlags) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct { const CommandImpl = struct {
const Impl = @This(); const Impl = @This();
@@ -838,6 +939,33 @@ pub fn resetEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineS
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
} }
pub fn resolveImage(interface: *Interface, src: *base.Image, _: vk.ImageLayout, dst: *base.Image, _: vk.ImageLayout, region: vk.ImageResolve) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
src: *const SoftImage,
dst: *SoftImage,
region: vk.ImageResolve,
pub fn execute(context: *anyopaque, _: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
try blitter.resolve(impl.src, impl.dst, impl.region);
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.src = @alignCast(@fieldParentPtr("interface", src)),
.dst = @alignCast(@fieldParentPtr("interface", dst)),
.region = region,
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void { pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineStageFlags) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
@@ -863,6 +991,31 @@ pub fn setEvent(interface: *Interface, event: *base.Event, stage: vk.PipelineSta
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory; self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
} }
pub fn setScissor(interface: *Interface, first: u32, scissor: []const vk.Rect2D) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
first: u32,
scissor: []const vk.Rect2D,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.dynamic_state.scissor = impl.scissor; // Unsafe
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.first = first,
.scissor = allocator.dupe(vk.Rect2D, scissor) catch return VkError.OutOfHostMemory, // Will be freed on cmdbuf reset or destroy
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setViewport(interface: *Interface, first: u32, viewports: []const vk.Viewport) VkError!void { pub fn setViewport(interface: *Interface, first: u32, viewports: []const vk.Viewport) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator(); const allocator = self.command_allocator.allocator();
+240 -15
View File
@@ -5,9 +5,12 @@ const base = @import("base");
const VkError = base.VkError; const VkError = base.VkError;
const Device = base.Device; const Device = base.Device;
const Buffer = base.Buffer; const Buffer = base.Buffer;
const BufferView = base.BufferView;
const ImageView = base.ImageView; const ImageView = base.ImageView;
const Sampler = base.Sampler;
const SoftBuffer = @import("SoftBuffer.zig"); const SoftBuffer = @import("SoftBuffer.zig");
const SoftBufferView = @import("SoftBufferView.zig");
const SoftImageView = @import("SoftImageView.zig"); const SoftImageView = @import("SoftImageView.zig");
const SoftSampler = @import("SoftSampler.zig"); const SoftSampler = @import("SoftSampler.zig");
@@ -31,10 +34,15 @@ const DescriptorImage = struct {
object: ?*SoftImageView, object: ?*SoftImageView,
}; };
const DescriptorTexel = struct {
object: ?*SoftBufferView,
};
const Descriptor = union(enum) { const Descriptor = union(enum) {
buffer: []DescriptorBuffer, buffer: []DescriptorBuffer,
texture: []DescriptorTexture, texture: []DescriptorTexture,
image: []DescriptorImage, image: []DescriptorImage,
texel_buffer: []DescriptorTexel,
unsupported: struct {}, unsupported: struct {},
}; };
@@ -61,8 +69,22 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
var size: usize = layout.bindings.len * @sizeOf(Descriptor); var size: usize = layout.bindings.len * @sizeOf(Descriptor);
for (layout.bindings) |binding| { for (layout.bindings) |binding| {
const struct_size: usize = switch (binding.descriptor_type) { const struct_size: usize = switch (binding.descriptor_type) {
.storage_buffer, .storage_buffer_dynamic => @sizeOf(DescriptorBuffer), .uniform_buffer,
.storage_image, .input_attachment => @sizeOf(DescriptorImage), .storage_buffer,
.storage_buffer_dynamic,
=> @sizeOf(DescriptorBuffer),
.storage_image,
.input_attachment,
=> @sizeOf(DescriptorImage),
.storage_texel_buffer,
.uniform_texel_buffer,
=> @sizeOf(DescriptorTexel),
.combined_image_sampler,
=> @sizeOf(DescriptorTexture),
else => 0, else => 0,
}; };
@@ -80,13 +102,62 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
const descriptors = local_allocator.alloc(Descriptor, layout.bindings.len) catch return VkError.OutOfHostMemory; const descriptors = local_allocator.alloc(Descriptor, layout.bindings.len) catch return VkError.OutOfHostMemory;
for (descriptors, layout.bindings) |*descriptor, binding| { for (descriptors, layout.bindings) |*descriptor, binding| {
switch (binding.descriptor_type) { switch (binding.descriptor_type) {
.storage_buffer, .storage_buffer_dynamic => descriptor.* = .{ .uniform_buffer,
.buffer = local_allocator.alloc(DescriptorBuffer, binding.array_size) catch return VkError.OutOfHostMemory, .storage_buffer,
.storage_buffer_dynamic,
=> descriptor.* = blk: {
const desc: Descriptor = .{
.buffer = local_allocator.alloc(DescriptorBuffer, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.buffer[0..]) |*d| {
d.* = .{
.object = null,
.offset = 0,
.size = 0,
};
}
break :blk desc;
}, },
.storage_image, .input_attachment => descriptor.* = .{
.image = local_allocator.alloc(DescriptorImage, binding.array_size) catch return VkError.OutOfHostMemory, .storage_image,
.input_attachment,
=> descriptor.* = blk: {
const desc: Descriptor = .{
.image = local_allocator.alloc(DescriptorImage, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.image[0..]) |*d| {
d.* = .{ .object = null };
}
break :blk desc;
}, },
else => {},
.storage_texel_buffer,
.uniform_texel_buffer,
=> descriptor.* = blk: {
const desc: Descriptor = .{
.texel_buffer = local_allocator.alloc(DescriptorTexel, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.texel_buffer[0..]) |*d| {
d.* = .{ .object = null };
}
break :blk desc;
},
.combined_image_sampler,
=> descriptor.* = blk: {
const desc: Descriptor = .{
.texture = local_allocator.alloc(DescriptorTexture, binding.array_size) catch return VkError.OutOfHostMemory,
};
for (desc.texture[0..]) |*d| {
d.* = .{
.sampler = null,
.view = null,
};
}
break :blk desc;
},
else => descriptor.* = .{ .unsupported = .{} },
} }
} }
@@ -98,23 +169,141 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, layout: *base.
return self; return self;
} }
pub fn copy(interface: *Interface, copy_data: vk.CopyDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
_ = self;
_ = copy_data;
}
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
allocator.free(self.heap); allocator.free(self.heap);
allocator.destroy(self); allocator.destroy(self);
} }
fn descriptorLen(descriptor: Descriptor) usize {
return switch (descriptor) {
.buffer => |buffer| buffer.len,
.image => |image| image.len,
.texel_buffer => |texel_buffer| texel_buffer.len,
.texture => |texture| texture.len,
.unsupported => 0,
};
}
fn advanceToAvailableDescriptor(descriptors: []const Descriptor, binding: *usize, array_element: *usize) bool {
while (binding.* < descriptors.len) {
switch (descriptors[binding.*]) {
.unsupported => return true,
else => {},
}
const len = descriptorLen(descriptors[binding.*]);
if (array_element.* < len) return true;
if (array_element.* > len) return false;
binding.* += 1;
array_element.* = 0;
}
return false;
}
fn copyDescriptorRange(dst_desc: *Descriptor, dst_array_element: usize, src_desc: Descriptor, src_array_element: usize, descriptor_count: usize) bool {
switch (dst_desc.*) {
.buffer => |dst_buffer| {
const src_buffer = switch (src_desc) {
.buffer => |buffer| buffer,
else => return false,
};
@memcpy(
dst_buffer[dst_array_element .. dst_array_element + descriptor_count],
src_buffer[src_array_element .. src_array_element + descriptor_count],
);
},
.image => |dst_image| {
const src_image = switch (src_desc) {
.image => |image| image,
else => return false,
};
@memcpy(
dst_image[dst_array_element .. dst_array_element + descriptor_count],
src_image[src_array_element .. src_array_element + descriptor_count],
);
},
.texel_buffer => |dst_texel_buffer| {
const src_texel_buffer = switch (src_desc) {
.texel_buffer => |texel_buffer| texel_buffer,
else => return false,
};
@memcpy(
dst_texel_buffer[dst_array_element .. dst_array_element + descriptor_count],
src_texel_buffer[src_array_element .. src_array_element + descriptor_count],
);
},
.texture => |dst_texture| {
const src_texture = switch (src_desc) {
.texture => |texture| texture,
else => return false,
};
@memcpy(
dst_texture[dst_array_element .. dst_array_element + descriptor_count],
src_texture[src_array_element .. src_array_element + descriptor_count],
);
},
.unsupported => return false,
}
return true;
}
pub fn copy(interface: *Interface, src_interface: *const Interface, data: vk.CopyDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const src: *const Self = @alignCast(@fieldParentPtr("interface", src_interface));
var dst_binding: usize = @intCast(data.dst_binding);
var src_binding: usize = @intCast(data.src_binding);
var dst_array_element: usize = @intCast(data.dst_array_element);
var src_array_element: usize = @intCast(data.src_array_element);
var descriptor_count: usize = @intCast(data.descriptor_count);
while (descriptor_count > 0) {
if (!advanceToAvailableDescriptor(self.descriptors, &dst_binding, &dst_array_element) or
!advanceToAvailableDescriptor(src.descriptors, &src_binding, &src_array_element))
{
return;
}
const dst_desc = &self.descriptors[dst_binding];
const src_desc = src.descriptors[src_binding];
const dst_len = descriptorLen(dst_desc.*);
const src_len = descriptorLen(src_desc);
if (dst_len == 0 or src_len == 0) {
base.unsupported("descriptor type for copy", .{});
return;
}
const dst_remaining = dst_len - dst_array_element;
const src_remaining = src_len - src_array_element;
const copy_count = @min(descriptor_count, dst_remaining, src_remaining);
if (!copyDescriptorRange(dst_desc, dst_array_element, src_desc, src_array_element, copy_count)) {
base.unsupported("descriptor type for copy", .{});
return;
}
descriptor_count -= copy_count;
dst_array_element += copy_count;
src_array_element += copy_count;
}
}
pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!void { pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
switch (write_data.descriptor_type) { switch (write_data.descriptor_type) {
.storage_buffer, .storage_buffer_dynamic => { .uniform_buffer,
.storage_buffer,
.storage_buffer_dynamic,
=> {
for (write_data.p_buffer_info, 0..write_data.descriptor_count) |buffer_info, i| { for (write_data.p_buffer_info, 0..write_data.descriptor_count) |buffer_info, i| {
const desc = &self.descriptors[write_data.dst_binding].buffer[i]; const desc = &self.descriptors[write_data.dst_binding].buffer[i];
desc.* = .{ desc.* = .{
@@ -131,7 +320,10 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
} }
} }
}, },
.storage_image, .input_attachment => {
.storage_image,
.input_attachment,
=> {
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| { for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
const desc = &self.descriptors[write_data.dst_binding].image[i]; const desc = &self.descriptors[write_data.dst_binding].image[i];
desc.* = .{ .object = null }; desc.* = .{ .object = null };
@@ -141,6 +333,39 @@ pub fn write(interface: *Interface, write_data: vk.WriteDescriptorSet) VkError!v
} }
} }
}, },
.storage_texel_buffer,
.uniform_texel_buffer,
=> {
for (write_data.p_texel_buffer_view, 0..write_data.descriptor_count) |view, i| {
const desc = &self.descriptors[write_data.dst_binding].texel_buffer[i];
desc.* = .{ .object = null };
if (view != .null_handle) {
const buffer_view = try NonDispatchable(BufferView).fromHandleObject(view);
desc.object = @as(*SoftBufferView, @alignCast(@fieldParentPtr("interface", buffer_view)));
}
}
},
.combined_image_sampler,
=> {
for (write_data.p_image_info, 0..write_data.descriptor_count) |image_info, i| {
const desc = &self.descriptors[write_data.dst_binding].texture[i];
desc.* = .{
.sampler = null,
.view = null,
};
if (image_info.image_view != .null_handle) {
const image_view = try NonDispatchable(ImageView).fromHandleObject(image_info.image_view);
desc.view = @as(*SoftImageView, @alignCast(@fieldParentPtr("interface", image_view)));
}
if (image_info.sampler != .null_handle) {
const sampler = try NonDispatchable(Sampler).fromHandleObject(image_info.sampler);
desc.sampler = @as(*SoftSampler, @alignCast(@fieldParentPtr("interface", sampler)));
}
}
},
else => { else => {
self.descriptors[write_data.dst_binding] = .{ .unsupported = .{} }; self.descriptors[write_data.dst_binding] = .{ .unsupported = .{} };
base.unsupported("descriptor type {s} for writting", .{@tagName(write_data.descriptor_type)}); base.unsupported("descriptor type {s} for writting", .{@tagName(write_data.descriptor_type)});
+2 -5
View File
@@ -13,9 +13,6 @@ interface: Interface,
data: []u8, data: []u8,
pub fn create(device: *SoftDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self { pub fn create(device: *SoftDevice, allocator: std.mem.Allocator, size: vk.DeviceSize, memory_type_index: u32) VkError!*Self {
if (size > lib.MAX_MEMORY_ALLOCATION_SIZE)
return VkError.OutOfDeviceMemory;
const self = allocator.create(Self) catch return VkError.OutOfHostMemory; const self = allocator.create(Self) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(self); errdefer allocator.destroy(self);
@@ -57,12 +54,12 @@ pub fn invalidateRange(interface: *Interface, offset: vk.DeviceSize, size: vk.De
_ = size; _ = size;
} }
pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!?*anyopaque { pub fn map(interface: *Interface, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
if (offset >= self.data.len or (size != vk.WHOLE_SIZE and offset + size > self.data.len)) { if (offset >= self.data.len or (size != vk.WHOLE_SIZE and offset + size > self.data.len)) {
return VkError.MemoryMapFailed; return VkError.MemoryMapFailed;
} }
return @ptrCast(&self.data[offset]); return if (size == vk.WHOLE_SIZE) self.data[offset..] else self.data[offset..(offset + size)];
} }
pub fn unmap(_: *Interface) void { pub fn unmap(_: *Interface) void {
+47
View File
@@ -5,6 +5,11 @@ const base = @import("base");
const VkError = base.VkError; const VkError = base.VkError;
const Device = base.Device; const Device = base.Device;
const blitter = @import("device/blitter.zig");
const SoftImage = @import("SoftImage.zig");
const SoftRenderPass = @import("SoftRenderPass.zig");
const Self = @This(); const Self = @This();
pub const Interface = base.Framebuffer; pub const Interface = base.Framebuffer;
@@ -30,3 +35,45 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
allocator.destroy(self); allocator.destroy(self);
} }
pub fn resolveAttachments(self: *Self, render_pass: *SoftRenderPass, subpass_index: usize) VkError!void {
const subpass = render_pass.interface.subpasses[subpass_index];
if (subpass.resolve_attachments) |resolve_attachments| {
if (subpass.color_attachments) |color_attachments| {
for (color_attachments[0..], resolve_attachments[0..]) |color, resolve| {
if (resolve.attachment != vk.ATTACHMENT_UNUSED) {
const src_image_view = self.interface.attachments[color.attachment];
const src_image: *SoftImage = @alignCast(@fieldParentPtr("interface", src_image_view.image));
const dst_image_view = self.interface.attachments[resolve.attachment];
const dst_image: *SoftImage = @alignCast(@fieldParentPtr("interface", dst_image_view.image));
try blitter.resolveWithFormats(
src_image,
dst_image,
.{
.src_subresource = .{
.aspect_mask = src_image_view.subresource_range.aspect_mask,
.base_array_layer = src_image_view.subresource_range.base_array_layer,
.layer_count = src_image_view.subresource_range.layer_count,
.mip_level = src_image_view.subresource_range.base_mip_level,
},
.src_offset = .{ .x = 0, .y = 0, .z = 0 },
.dst_subresource = .{
.aspect_mask = dst_image_view.subresource_range.aspect_mask,
.base_array_layer = dst_image_view.subresource_range.base_array_layer,
.layer_count = dst_image_view.subresource_range.layer_count,
.mip_level = dst_image_view.subresource_range.base_mip_level,
},
.dst_offset = .{ .x = 0, .y = 0, .z = 0 },
.extent = src_image.getMipLevelExtent(src_image_view.subresource_range.base_mip_level),
},
src_image_view.format,
dst_image_view.format,
);
}
}
}
}
}
+97 -44
View File
@@ -1,3 +1,55 @@
//! Image layout representation in contiguous memory
//!
//! ```text
//! ┌──────────────────────────────────────────────────┐
//! │ Device memory at image offset │
//! │ ┌──────────────────────────────────────────────┐ │
//! │ │ Aspect 0, e.g. color/depth │ │
//! │ │ │ │
//! │ │ Layer 0 │ │
//! │ │ ┌────────────────────────────────────────┐ │ │
//! │ │ │ Mip 0 │ │ │
//! │ │ │ ┌──────────────────────────────────┐ │ │ │
//! │ │ │ │ z=0 slice │ │ │ │
//! │ │ │ │ row 0: [px][px][px][px] │ │ │ │
//! │ │ │ │ row 1: [px][px][px][px] │ │ │ │
//! │ │ │ │ row 2: [px][px][px][px] │ │ │ │
//! │ │ │ └──────────────────────────────────┘ │ │ │
//! │ │ │ │ │ │
//! │ │ │ Mip 1 │ │ │
//! │ │ │ ┌──────────────────────────────────┐ │ │ │
//! │ │ │ │ row 0: [px][px] │ │ │ │
//! │ │ │ │ row 1: [px][px] │ │ │ │
//! │ │ │ └──────────────────────────────────┘ │ │ │
//! │ │ │ │ │ │
//! │ │ │ Mip 2 │ │ │
//! │ │ │ ┌──────────────────────────────────┐ │ │ │
//! │ │ │ │ row 0: [px] │ │ │ │
//! │ │ │ └──────────────────────────────────┘ │ │ │
//! │ │ └────────────────────────────────────────┘ │ │
//! │ │ │ │
//! │ │ Layer 1 │ │
//! │ │ ┌────────────────────────────────────────┐ │ │
//! │ │ │ Mip 0 │ │ │
//! │ │ │ row 0: [px][px][px][px] │ │ │
//! │ │ │ row 1: [px][px][px][px] │ │ │
//! │ │ │ row 2: [px][px][px][px] │ │ │
//! │ │ │ │ │ │
//! │ │ │ Mip 1 │ │ │
//! │ │ │ row 0: [px][px] │ │ │
//! │ │ │ row 1: [px][px] │ │ │
//! │ │ │ │ │ │
//! │ │ │ Mip 2 │ │ │
//! │ │ │ row 0: [px] │ │ │
//! │ │ └────────────────────────────────────────┘ │ │
//! │ └──────────────────────────────────────────────┘ │
//! │ ┌──────────────────────────────────────────────┐ │
//! │ │ Aspect 1, e.g. stencil │ │
//! │ │ ... │ │
//! │ └──────────────────────────────────────────────┘ │
//! └──────────────────────────────────────────────────┘
//! ```
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
@@ -50,15 +102,18 @@ pub fn getMemoryRequirements(_: *Interface, requirements: *vk.MemoryRequirements
} }
pub fn getClearFormat(self: *Self) VkError!vk.Format { pub fn getClearFormat(self: *Self) VkError!vk.Format {
return if (base.c.vkuFormatIsSINT(@intCast(@intFromEnum(self.interface.format)))) return getClearFormatFor(self.interface.format);
}
pub fn getClearFormatFor(format: vk.Format) VkError!vk.Format {
return if (base.format.isSint(format))
.r32g32b32a32_sint .r32g32b32a32_sint
else if (base.c.vkuFormatIsUINT(@intCast(@intFromEnum(self.interface.format)))) else if (base.format.isUint(format))
.r32g32b32a32_uint .r32g32b32a32_uint
else else
.r32g32b32a32_sfloat; .r32g32b32a32_sfloat;
} }
/// Based on SwiftShader vk::Image::copyTo
pub fn copyToImage(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void { pub fn copyToImage(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void {
const combined_depth_stencil_aspect: vk.ImageAspectFlags = .{ const combined_depth_stencil_aspect: vk.ImageAspectFlags = .{
.depth_bit = true, .depth_bit = true,
@@ -82,7 +137,6 @@ pub fn copyToImage(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!
} }
} }
/// Based on SwiftShader vk::Image::copySingleAspectTo
pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void { pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCopy) VkError!void {
if (!(region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .color_bit = true } or if (!(region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .color_bit = true } or
region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .depth_bit = true } or region.src_subresource.aspect_mask == vk.ImageAspectFlags{ .depth_bit = true } or
@@ -104,7 +158,7 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
const bytes_per_block = base.format.texelSize(src_format); const bytes_per_block = base.format.texelSize(src_format);
const src_extent = self.getMipLevelExtent(region.src_subresource.mip_level); const src_extent = self.getMipLevelExtent(region.src_subresource.mip_level);
const dst_extent = dst.getMipLevelExtent(region.src_subresource.mip_level); const dst_extent = dst.getMipLevelExtent(region.dst_subresource.mip_level);
const one_is_3D = (self.interface.image_type == .@"3d") != (dst.interface.image_type == .@"3d"); const one_is_3D = (self.interface.image_type == .@"3d") != (dst.interface.image_type == .@"3d");
const both_are_3D = (self.interface.image_type == .@"3d") and (dst.interface.image_type == .@"3d"); const both_are_3D = (self.interface.image_type == .@"3d") and (dst.interface.image_type == .@"3d");
@@ -120,24 +174,13 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
const src_layer_pitch = if (self.interface.image_type == .@"3d") src_depth_pitch_bytes else src_array_pitch; const src_layer_pitch = if (self.interface.image_type == .@"3d") src_depth_pitch_bytes else src_array_pitch;
const dst_layer_pitch = if (dst.interface.image_type == .@"3d") dst_depth_pitch_bytes else dst_array_pitch; const dst_layer_pitch = if (dst.interface.image_type == .@"3d") dst_depth_pitch_bytes else dst_array_pitch;
// If one image is 3D, extent.depth must match the layer count. If both images are 2D,
// depth is 1 but the source and destination subresource layer count must match.
const layer_count = if (one_is_3D) region.extent.depth else region.src_subresource.layer_count; const layer_count = if (one_is_3D) region.extent.depth else region.src_subresource.layer_count;
// Copies between 2D and 3D images are treated as layers, so only use depth as the slice count when both images are 3D.
const slice_count = if (both_are_3D) region.extent.depth else self.interface.samples.toInt(); const slice_count = if (both_are_3D) region.extent.depth else self.interface.samples.toInt();
const is_single_slice = (slice_count == 1); const is_single_slice = (slice_count == 1);
const is_single_row = (region.extent.height == 1) and is_single_slice; const is_single_row = (region.extent.height == 1) and is_single_slice;
// In order to copy multiple rows using a single memcpy call, we
// have to make sure that we need to copy the entire row and that
// both source and destination rows have the same size in bytes
const is_entire_row = (region.extent.width == src_extent.width) and (region.extent.width == dst_extent.width); const is_entire_row = (region.extent.width == src_extent.width) and (region.extent.width == dst_extent.width);
// In order to copy multiple slices using a single memcpy call, we
// have to make sure that we need to copy the entire slice and that
// both source and destination slices have the same size in bytes
const is_entire_slice = is_entire_row and const is_entire_slice = is_entire_row and
(region.extent.height == src_extent.height) and (region.extent.height == src_extent.height) and
(region.extent.height == dst_extent.height) and (region.extent.height == dst_extent.height) and
@@ -148,16 +191,14 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
.mip_level = region.src_subresource.mip_level, .mip_level = region.src_subresource.mip_level,
.array_layer = region.src_subresource.base_array_layer, .array_layer = region.src_subresource.base_array_layer,
}); });
const src_size = try self.interface.getTotalSizeForAspect(region.src_subresource.aspect_mask); var src_map = try self.mapAsSliceWithAddedOffset(u8, src_texel_offset, vk.WHOLE_SIZE);
var src_map = try self.mapAsSliceWithAddedOffset(u8, src_texel_offset, src_size);
const dst_texel_offset = try dst.getTexelMemoryOffset(region.dst_offset, .{ const dst_texel_offset = try dst.getTexelMemoryOffset(region.dst_offset, .{
.aspect_mask = region.dst_subresource.aspect_mask, .aspect_mask = region.dst_subresource.aspect_mask,
.mip_level = region.dst_subresource.mip_level, .mip_level = region.dst_subresource.mip_level,
.array_layer = region.dst_subresource.base_array_layer, .array_layer = region.dst_subresource.base_array_layer,
}); });
const dst_size = try dst.interface.getTotalSizeForAspect(region.dst_subresource.aspect_mask); var dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, vk.WHOLE_SIZE);
var dst_map = try dst.mapAsSliceWithAddedOffset(u8, dst_texel_offset, dst_size);
for (0..layer_count) |_| { for (0..layer_count) |_| {
if (is_single_row) { if (is_single_row) {
@@ -203,6 +244,9 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
src_row_memory = if (src_row_memory.len < src_row_pitch_bytes) break else src_row_memory[src_row_pitch_bytes..]; src_row_memory = if (src_row_memory.len < src_row_pitch_bytes) break else src_row_memory[src_row_pitch_bytes..];
dst_row_memory = if (dst_row_memory.len < dst_row_pitch_bytes) break else dst_row_memory[dst_row_pitch_bytes..]; dst_row_memory = if (dst_row_memory.len < dst_row_pitch_bytes) break else dst_row_memory[dst_row_pitch_bytes..];
} }
src_slice_memory = if (src_slice_memory.len < src_depth_pitch_bytes) break else src_slice_memory[src_depth_pitch_bytes..];
dst_slice_memory = if (dst_slice_memory.len < dst_depth_pitch_bytes) break else dst_slice_memory[dst_depth_pitch_bytes..];
} }
} }
@@ -212,37 +256,38 @@ pub fn copyToImageSingleAspect(self: *const Self, dst: *Self, region: vk.ImageCo
} }
pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageCopy) VkError!void { pub fn copyToBuffer(self: *const Self, dst: *SoftBuffer, region: vk.BufferImageCopy) VkError!void {
const dst_size = dst.interface.size - region.buffer_offset;
const dst_offset = dst.interface.offset + region.buffer_offset; const dst_offset = dst.interface.offset + region.buffer_offset;
const dst_map = try dst.mapAsSliceWithOffset(u8, dst_offset, dst_size); const dst_map = try dst.mapAsSliceWithOffset(u8, dst_offset, vk.WHOLE_SIZE);
try self.copy( try self.copy(
null, null,
dst_map, dst_map,
region.image_subresource, region.image_subresource,
region.image_offset, region.image_offset,
region.image_extent, region.image_extent,
region.buffer_row_length,
region.buffer_image_height,
); );
} }
pub fn copyFromBuffer(self: *const Self, src: *const SoftBuffer, region: vk.BufferImageCopy) VkError!void { pub fn copyFromBuffer(self: *const Self, src: *const SoftBuffer, region: vk.BufferImageCopy) VkError!void {
const src_size = src.interface.size - region.buffer_offset;
const src_offset = src.interface.offset + region.buffer_offset; const src_offset = src.interface.offset + region.buffer_offset;
const src_map = try src.mapAsSliceWithOffset(u8, src_offset, src_size); const src_map = try src.mapAsSliceWithOffset(u8, src_offset, vk.WHOLE_SIZE);
try self.copy( try self.copy(
src_map, src_map,
null, null,
region.image_subresource, region.image_subresource,
region.image_offset, region.image_offset,
region.image_extent, region.image_extent,
region.buffer_row_length,
region.buffer_image_height,
); );
} }
pub fn copyToMemory(interface: *const Interface, memory: []u8, subresource: vk.ImageSubresourceLayers) VkError!void { pub fn copyToMemory(interface: *const Interface, memory: []u8, subresource: vk.ImageSubresourceLayers) VkError!void {
const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *const Self = @alignCast(@fieldParentPtr("interface", interface));
try self.copy(null, memory, subresource, .{ .x = 0, .y = 0, .z = 0 }, interface.extent); try self.copy(null, memory, subresource, .{ .x = 0, .y = 0, .z = 0 }, interface.extent, 0, 0);
} }
/// Based on SwiftShader vk::Image::copy
pub fn copy( pub fn copy(
self: *const Self, self: *const Self,
base_src_memory: ?[]const u8, base_src_memory: ?[]const u8,
@@ -250,6 +295,8 @@ pub fn copy(
image_subresource: vk.ImageSubresourceLayers, image_subresource: vk.ImageSubresourceLayers,
image_offset: vk.Offset3D, image_offset: vk.Offset3D,
image_extent: vk.Extent3D, image_extent: vk.Extent3D,
row_length: u32,
image_height: u32,
) VkError!void { ) VkError!void {
std.debug.assert((base_src_memory == null) != (base_dst_memory == null)); std.debug.assert((base_src_memory == null) != (base_dst_memory == null));
@@ -266,23 +313,25 @@ pub fn copy(
const format = self.interface.formatFromAspect(image_subresource.aspect_mask); const format = self.interface.formatFromAspect(image_subresource.aspect_mask);
// TODO: handle extent of compressed formats
if (image_extent.width == 0 or image_extent.height == 0 or image_extent.depth == 0) { if (image_extent.width == 0 or image_extent.height == 0 or image_extent.depth == 0) {
return; return;
} }
const extent: vk.Extent2D = .{
.width = if (row_length == 0) image_extent.width else row_length,
.height = if (image_height == 0) image_extent.height else image_height,
};
const bytes_per_block = base.format.texelSize(format); const bytes_per_block = base.format.texelSize(format);
const memory_row_pitch_bytes = image_extent.width * bytes_per_block; const memory_row_pitch_bytes = extent.width * bytes_per_block;
const memory_slice_pitch_bytes = image_extent.height * memory_row_pitch_bytes; const memory_slice_pitch_bytes = extent.height * memory_row_pitch_bytes;
const image_texel_offset = try self.getTexelMemoryOffset(image_offset, .{ const image_texel_offset = try self.getTexelMemoryOffset(image_offset, .{
.aspect_mask = image_subresource.aspect_mask, .aspect_mask = image_subresource.aspect_mask,
.mip_level = image_subresource.mip_level, .mip_level = image_subresource.mip_level,
.array_layer = image_subresource.base_array_layer, .array_layer = image_subresource.base_array_layer,
}); });
const image_size = try self.interface.getTotalSizeForAspect(image_subresource.aspect_mask); const image_map = try self.mapAsSliceWithAddedOffset(u8, image_texel_offset, vk.WHOLE_SIZE);
const image_map = try self.mapAsSliceWithAddedOffset(u8, image_texel_offset, image_size);
var src_memory = if (is_source) base_src_memory orelse return VkError.InvalidDeviceMemoryDrv else image_map; var src_memory = if (is_source) base_src_memory orelse return VkError.InvalidDeviceMemoryDrv else image_map;
var dst_memory = if (is_source) image_map else base_dst_memory orelse return VkError.InvalidDeviceMemoryDrv; var dst_memory = if (is_source) image_map else base_dst_memory orelse return VkError.InvalidDeviceMemoryDrv;
@@ -360,7 +409,7 @@ pub fn getTexelMemoryOffset(self: *const Self, offset: vk.Offset3D, subresource:
return try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer) + self.getTexelMemoryOffsetInSubresource(offset, subresource); return try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer) + self.getTexelMemoryOffsetInSubresource(offset, subresource);
} }
fn getSubresourceOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, layer: u32) VkError!usize { pub fn getSubresourceOffset(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, layer: u32) VkError!usize {
var offset = try self.getAspectOffset(aspect_mask); var offset = try self.getAspectOffset(aspect_mask);
for (0..mip_level) |mip| { for (0..mip_level) |mip| {
offset += self.getMultiSampledLevelSize(aspect_mask, @intCast(mip)); offset += self.getMultiSampledLevelSize(aspect_mask, @intCast(mip));
@@ -427,8 +476,8 @@ fn getSubresourceLayout(interface: *const Interface, subresource: vk.ImageSubres
.offset = try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer), .offset = try self.getSubresourceOffset(subresource.aspect_mask, subresource.mip_level, subresource.array_layer),
.size = self.getMultiSampledLevelSize(subresource.aspect_mask, subresource.mip_level), .size = self.getMultiSampledLevelSize(subresource.aspect_mask, subresource.mip_level),
.row_pitch = self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), .row_pitch = self.interface.getRowPitchMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level),
.array_pitch = self.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level), .array_pitch = self.getLayerSize(subresource.aspect_mask),
.depth_pitch = self.getLayerSize(subresource.aspect_mask), .depth_pitch = self.interface.getSliceMemSizeForMipLevel(subresource.aspect_mask, subresource.mip_level),
}; };
} }
@@ -464,18 +513,22 @@ pub fn getMipLevelExtent(self: *const Self, mip_level: u32) vk.Extent3D {
pub fn getSliceMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { pub fn getSliceMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *const Self = @alignCast(@fieldParentPtr("interface", interface));
return self.getSliceMemSizeForMipLevelWithFormat(aspect_mask, mip_level, interface.format);
const mip_extent = self.getMipLevelExtent(mip_level);
const format = self.interface.formatFromAspect(aspect_mask);
return base.format.sliceMemSize(format, mip_extent.width, mip_extent.height);
} }
pub fn getRowPitchMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize { pub fn getRowPitchMemSizeForMipLevel(interface: *const Interface, aspect_mask: vk.ImageAspectFlags, mip_level: u32) usize {
const self: *const Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *const Self = @alignCast(@fieldParentPtr("interface", interface));
return self.getRowPitchMemSizeForMipLevelWithFormat(aspect_mask, mip_level, interface.format);
}
pub fn getSliceMemSizeForMipLevelWithFormat(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, format: vk.Format) usize {
const mip_extent = self.getMipLevelExtent(mip_level); const mip_extent = self.getMipLevelExtent(mip_level);
const format = self.interface.formatFromAspect(aspect_mask); return base.format.sliceMemSize(base.format.fromAspect(format, aspect_mask), mip_extent.width, mip_extent.height);
return base.format.pitchMemSize(format, mip_extent.width); }
pub fn getRowPitchMemSizeForMipLevelWithFormat(self: *const Self, aspect_mask: vk.ImageAspectFlags, mip_level: u32, format: vk.Format) usize {
const mip_extent = self.getMipLevelExtent(mip_level);
return base.format.pitchMemSize(base.format.fromAspect(format, aspect_mask), mip_extent.width);
} }
pub inline fn mapAs(self: *const Self, comptime T: type) VkError!*T { pub inline fn mapAs(self: *const Self, comptime T: type) VkError!*T {
@@ -504,18 +557,18 @@ pub inline fn mapAsSliceWithAddedOffset(self: *const Self, comptime T: type, off
pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T { pub fn mapAsWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!*T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; const map = try memory.map(offset, @sizeOf(T));
return @alignCast(std.mem.bytesAsValue(T, map)); return @alignCast(std.mem.bytesAsValue(T, map));
} }
pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T { pub fn mapToWithOffset(self: *const Self, comptime T: type, offset: usize) VkError!T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, @sizeOf(T)))))[0..@sizeOf(T)]; const map = try memory.map(offset, @sizeOf(T));
return std.mem.bytesToValue(T, map); return std.mem.bytesToValue(T, map);
} }
pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T { pub fn mapAsSliceWithOffset(self: *const Self, comptime T: type, offset: usize, size: usize) VkError![]T {
const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const memory = if (self.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const map = @as([*]u8, @ptrCast(@alignCast(try memory.map(offset, size))))[0..size]; const map = try memory.map(offset, size);
return @alignCast(std.mem.bytesAsSlice(T, map)); return @alignCast(std.mem.bytesAsSlice(T, map));
} }
-2
View File
@@ -2,8 +2,6 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const lib = @import("lib.zig");
const VkError = base.VkError; const VkError = base.VkError;
const Device = base.Device; const Device = base.Device;
+110 -51
View File
@@ -1,8 +1,8 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const lib = @import("lib.zig"); const lib = @import("lib.zig");
const cpuinfo = lib.c;
const SoftDevice = @import("SoftDevice.zig"); const SoftDevice = @import("SoftDevice.zig");
@@ -68,8 +68,8 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
.max_texel_buffer_elements = 65536, .max_texel_buffer_elements = 65536,
.max_uniform_buffer_range = 16384, .max_uniform_buffer_range = 16384,
.max_storage_buffer_range = 134217728, .max_storage_buffer_range = 134217728,
.max_push_constants_size = 128, .max_push_constants_size = lib.PUSH_CONSTANT_SIZE,
.max_memory_allocation_count = lib.MAX_ALLOCATION_COUNT, .max_memory_allocation_count = std.math.maxInt(u32),
.max_sampler_allocation_count = 4096, .max_sampler_allocation_count = 4096,
.buffer_image_granularity = 131072, .buffer_image_granularity = 131072,
.sparse_address_space_size = 0, .sparse_address_space_size = 0,
@@ -180,7 +180,7 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
}; };
interface.mem_props.memory_heap_count = 1; interface.mem_props.memory_heap_count = 1;
interface.mem_props.memory_heaps[0] = .{ interface.mem_props.memory_heaps[0] = .{
.size = lib.PHYSICAL_DEVICE_HEAP_SIZE, .size = std.process.totalSystemMemory() catch lib.PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE,
.flags = .{ .device_local_bit = true }, .flags = .{ .device_local_bit = true },
}; };
@@ -189,8 +189,6 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
.shader_float_64 = .true, .shader_float_64 = .true,
.shader_int_64 = .true, .shader_int_64 = .true,
.shader_int_16 = .true, .shader_int_16 = .true,
.texture_compression_etc2 = .true,
.texture_compression_bc = .true,
}; };
var queue_family_props = [_]vk.QueueFamilyProperties{ var queue_family_props = [_]vk.QueueFamilyProperties{
@@ -200,35 +198,52 @@ pub fn create(allocator: std.mem.Allocator, instance: *base.Instance) VkError!*S
.timestamp_valid_bits = 0, .timestamp_valid_bits = 0,
.min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 }, .min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 },
}, },
.{
.queue_flags = .{ .graphics_bit = true },
.queue_count = 1,
.timestamp_valid_bits = 0,
.min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 },
},
.{
.queue_flags = .{ .transfer_bit = true },
.queue_count = 1,
.timestamp_valid_bits = 0,
.min_image_transfer_granularity = .{ .width = 1, .height = 1, .depth = 1 },
},
// TODO: maybe add a compute specialized queue
}; };
interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory; interface.queue_family_props.appendSlice(allocator, queue_family_props[0..]) catch return VkError.OutOfHostMemory;
if (device_name[0] == 0) { if (device_name[0] == 0) {
const name = blk: { const name = blk: {
if (cpuinfo.cpuinfo_initialize()) {
const package = cpuinfo.cpuinfo_get_package(0).*; // If arch is x86 we try to get precise CPU name through CPUID
const non_sentinel_name = package.name[0..(std.mem.len(@as([*:0]const u8, @ptrCast(&package.name))))]; // and fallback to vendor name if not available
break :blk std.fmt.allocPrint(command_allocator, "{s} ({d} threads)", .{ non_sentinel_name, package.processor_count }) catch return VkError.OutOfHostMemory; if (comptime builtin.cpu.arch.isX86()) {
const max_extended_leaf = cpuid(0x80000000, 0).eax;
if (max_extended_leaf >= 0x80000004) {
var brand: [49]u8 = @splat(0);
for (0..3) |i| {
const regs = cpuid(0x80000002 + @as(u32, @intCast(i)), 0);
const offset = i * 16;
writeU32Le(brand[0..], offset + 0, regs.eax);
writeU32Le(brand[0..], offset + 4, regs.ebx);
writeU32Le(brand[0..], offset + 8, regs.ecx);
writeU32Le(brand[0..], offset + 12, regs.edx);
}
const brand_str = std.mem.trim(u8, brand[0..48], " \x00");
break :blk command_allocator.dupe(u8, brand_str) catch return VkError.OutOfHostMemory;
} else {
var vendor: [13]u8 = @splat(0);
const leaf0 = cpuid(0, 0);
writeU32Le(vendor[0..], 0, leaf0.ebx);
writeU32Le(vendor[0..], 4, leaf0.edx);
writeU32Le(vendor[0..], 8, leaf0.ecx);
const vendor_str = std.mem.trim(u8, vendor[0..12], " \x00");
break :blk command_allocator.dupe(u8, vendor_str) catch return VkError.OutOfHostMemory;
}
} }
break :blk command_allocator.dupe(u8, "Unkown") catch return VkError.OutOfHostMemory;
break :blk command_allocator.dupe(u8, lib.PHYSICAL_DEVICE_DEFAULT_NAME) catch return VkError.OutOfHostMemory;
}; };
defer command_allocator.free(name); defer command_allocator.free(name);
var writer = std.Io.Writer.fixed(device_name[0 .. vk.MAX_PHYSICAL_DEVICE_NAME_SIZE - 1]); var writer = std.Io.Writer.fixed(device_name[0 .. vk.MAX_PHYSICAL_DEVICE_NAME_SIZE - 1]);
writer.print("{s} [" ++ lib.DRIVER_NAME ++ " StrollDriver]", .{name}) catch return VkError.InitializationFailed; writer.print("{s} [" ++ lib.DRIVER_NAME ++ " ApeDriver]", .{name}) catch return VkError.InitializationFailed;
} }
@memcpy(&interface.props.device_name, &device_name); @memcpy(&interface.props.device_name, &device_name);
@@ -312,32 +327,32 @@ pub fn getFormatProperties(interface: *Interface, format: vk.Format) VkError!vk.
.r32g32b32a32_sfloat, .r32g32b32a32_sfloat,
.b10g11r11_ufloat_pack32, .b10g11r11_ufloat_pack32,
.e5b9g9r9_ufloat_pack32, .e5b9g9r9_ufloat_pack32,
.bc1_rgb_unorm_block, //.bc1_rgb_unorm_block,
.bc1_rgb_srgb_block, //.bc1_rgb_srgb_block,
.bc1_rgba_unorm_block, //.bc1_rgba_unorm_block,
.bc1_rgba_srgb_block, //.bc1_rgba_srgb_block,
.bc2_unorm_block, //.bc2_unorm_block,
.bc2_srgb_block, //.bc2_srgb_block,
.bc3_unorm_block, //.bc3_unorm_block,
.bc3_srgb_block, //.bc3_srgb_block,
.bc4_unorm_block, //.bc4_unorm_block,
.bc4_snorm_block, //.bc4_snorm_block,
.bc5_unorm_block, //.bc5_unorm_block,
.bc5_snorm_block, //.bc5_snorm_block,
.bc6h_ufloat_block, //.bc6h_ufloat_block,
.bc6h_sfloat_block, //.bc6h_sfloat_block,
.bc7_unorm_block, //.bc7_unorm_block,
.bc7_srgb_block, //.bc7_srgb_block,
.etc2_r8g8b8_unorm_block, //.etc2_r8g8b8_unorm_block,
.etc2_r8g8b8_srgb_block, //.etc2_r8g8b8_srgb_block,
.etc2_r8g8b8a1_unorm_block, //.etc2_r8g8b8a1_unorm_block,
.etc2_r8g8b8a1_srgb_block, //.etc2_r8g8b8a1_srgb_block,
.etc2_r8g8b8a8_unorm_block, //.etc2_r8g8b8a8_unorm_block,
.etc2_r8g8b8a8_srgb_block, //.etc2_r8g8b8a8_srgb_block,
.eac_r11_unorm_block, //.eac_r11_unorm_block,
.eac_r11_snorm_block, //.eac_r11_snorm_block,
.eac_r11g11_unorm_block, //.eac_r11g11_unorm_block,
.eac_r11g11_snorm_block, //.eac_r11g11_snorm_block,
//.astc_4x_4_unorm_block, //.astc_4x_4_unorm_block,
//.astc_5x_4_unorm_block, //.astc_5x_4_unorm_block,
//.astc_5x_5_unorm_block, //.astc_5x_5_unorm_block,
@@ -856,3 +871,47 @@ fn checkFormatUsage(usage: vk.ImageUsageFlags, features: vk.FormatFeatureFlags)
pub fn getSurfaceSupportKHR(_: *Interface, _: u32, _: *SurfaceKHR) VkError!bool { pub fn getSurfaceSupportKHR(_: *Interface, _: u32, _: *SurfaceKHR) VkError!bool {
return true; return true;
} }
const CpuidRegs = packed struct {
eax: u32,
ebx: u32,
ecx: u32,
edx: u32,
};
fn cpuid(leaf_id: u32, subleaf_id: u32) CpuidRegs {
comptime {
switch (builtin.cpu.arch) {
.x86, .x86_64 => {},
else => @compileError("cpuid is only available on x86/x86_64"),
}
}
var eax: u32 = undefined;
var ebx: u32 = undefined;
var ecx: u32 = undefined;
var edx: u32 = undefined;
asm volatile ("cpuid"
: [_] "={eax}" (eax),
[_] "={ebx}" (ebx),
[_] "={ecx}" (ecx),
[_] "={edx}" (edx),
: [_] "{eax}" (leaf_id),
[_] "{ecx}" (subleaf_id),
);
return .{
.eax = eax,
.ebx = ebx,
.ecx = ecx,
.edx = edx,
};
}
fn writeU32Le(dst: []u8, offset: usize, value: u32) void {
dst[offset + 0] = @truncate(value);
dst[offset + 1] = @truncate(value >> 8);
dst[offset + 2] = @truncate(value >> 16);
dst[offset + 3] = @truncate(value >> 24);
}
+408 -120
View File
@@ -3,6 +3,10 @@ const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const spv = @import("spv"); const spv = @import("spv");
const zm = base.zm;
const blitter = @import("device/blitter.zig");
const Device = base.Device; const Device = base.Device;
const VkError = base.VkError; const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError; const SpvRuntimeError = spv.Runtime.RuntimeError;
@@ -11,17 +15,25 @@ const NonDispatchable = base.NonDispatchable;
const ShaderModule = base.ShaderModule; const ShaderModule = base.ShaderModule;
const SoftDevice = @import("SoftDevice.zig"); const SoftDevice = @import("SoftDevice.zig");
const SoftBuffer = @import("SoftBuffer.zig");
const SoftBufferView = @import("SoftBufferView.zig");
const SoftImage = @import("SoftImage.zig"); const SoftImage = @import("SoftImage.zig");
const SoftImageView = @import("SoftImageView.zig"); const SoftImageView = @import("SoftImageView.zig");
const SoftInstance = @import("SoftInstance.zig"); const SoftInstance = @import("SoftInstance.zig");
const SoftSampler = @import("SoftSampler.zig");
const SoftShaderModule = @import("SoftShaderModule.zig"); const SoftShaderModule = @import("SoftShaderModule.zig");
const Self = @This(); const Self = @This();
pub const Interface = base.Pipeline; pub const Interface = base.Pipeline;
const Runtime = struct {
mutex: std.Io.Mutex,
rt: spv.Runtime,
};
const Shader = struct { const Shader = struct {
module: *SoftShaderModule, module: *SoftShaderModule,
runtimes: []spv.Runtime, runtimes: []Runtime,
entry: []const u8, entry: []const u8,
}; };
@@ -54,9 +66,13 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
const device_allocator = soft_device.device_allocator.allocator(); const device_allocator = soft_device.device_allocator.allocator();
var runtimes_allocator_arena: std.heap.ArenaAllocator = .init(device_allocator); self.* = .{
errdefer runtimes_allocator_arena.deinit(); .interface = interface,
const runtimes_allocator = runtimes_allocator_arena.allocator(); .runtimes_allocator = .init(device_allocator),
.stages = std.EnumMap(Stages, Shader).init(.{}),
};
errdefer self.runtimes_allocator.deinit();
const runtimes_allocator = self.runtimes_allocator.allocator();
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance)); const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
const runtimes_count = switch (instance.threaded.async_limit) { const runtimes_count = switch (instance.threaded.async_limit) {
@@ -68,57 +84,54 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
}, },
}; };
self.* = .{ self.stages.put(.compute, blk: {
.interface = interface, var shader: Shader = undefined;
.runtimes_allocator = runtimes_allocator_arena, soft_module.ref();
.stages = std.EnumMap(Stages, Shader).init(.{ shader.module = soft_module;
.compute = blk: {
var shader: Shader = undefined;
soft_module.ref();
shader.module = soft_module;
const runtimes = runtimes_allocator.alloc(spv.Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory; const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfDeviceMemory;
for (runtimes) |*runtime| { for (runtimes) |*runtime| {
runtime.* = spv.Runtime.init( runtime.mutex = .init;
runtimes_allocator, runtime.rt = spv.Runtime.init(
&soft_module.module, runtimes_allocator,
.{ &soft_module.module,
.readImageFloat4 = readImageFloat4, .{
.readImageInt4 = readImageInt4, .readImageFloat4 = readImageFloat4,
.writeImageFloat4 = writeImageFloat4, .readImageInt4 = readImageInt4,
.writeImageInt4 = writeImageInt4, .writeImageFloat4 = writeImageFloat4,
}, .writeImageInt4 = writeImageInt4,
) catch |err| { .sampleImageFloat4 = sampleImageFloat4,
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)}); .queryImageSize = queryImageSize,
return VkError.Unknown; },
}; ) catch |err| {
if (info.stage.p_specialization_info) |specialization| { std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
if (specialization.p_map_entries) |map| { return VkError.Unknown;
const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size]; };
for (map[0..], 0..specialization.map_entry_count) |entry, _| { if (info.stage.p_specialization_info) |specialization| {
runtime.addSpecializationInfo( if (specialization.p_map_entries) |map| {
runtimes_allocator, const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size];
.{ for (map[0..], 0..specialization.map_entry_count) |entry, _| {
.id = @intCast(entry.constant_id), runtime.rt.addSpecializationInfo(
.offset = @intCast(entry.offset), runtimes_allocator,
.size = @intCast(entry.size), .{
}, .id = @intCast(entry.constant_id),
data, .offset = @intCast(entry.offset),
) catch return VkError.OutOfDeviceMemory; .size = @intCast(entry.size),
} },
} data,
) catch return VkError.OutOfDeviceMemory;
} }
} }
}
}
shader.runtimes = runtimes; shader.runtimes = runtimes;
shader.entry = runtimes_allocator.dupe(u8, std.mem.span(info.stage.p_name)) catch return VkError.OutOfDeviceMemory; shader.entry = runtimes_allocator.dupe(u8, std.mem.span(info.stage.p_name)) catch return VkError.OutOfDeviceMemory;
std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count}); std.log.scoped(.ComputePipeline).debug("Created {d} runtimes for compute stage", .{runtimes_count});
break :blk shader; break :blk shader;
}, });
}),
};
return self; return self;
} }
@@ -135,9 +148,13 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device)); const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", device));
const device_allocator = soft_device.device_allocator.allocator(); const device_allocator = soft_device.device_allocator.allocator();
var runtimes_allocator_arena: std.heap.ArenaAllocator = .init(device_allocator); self.* = .{
errdefer runtimes_allocator_arena.deinit(); .interface = interface,
const runtimes_allocator = runtimes_allocator_arena.allocator(); .runtimes_allocator = .init(device_allocator),
.stages = std.EnumMap(Stages, Shader).init(.{}),
};
errdefer self.runtimes_allocator.deinit();
const runtimes_allocator = self.runtimes_allocator.allocator();
const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance)); const instance: *SoftInstance = @alignCast(@fieldParentPtr("interface", device.instance));
const runtimes_count = switch (instance.threaded.async_limit) { const runtimes_count = switch (instance.threaded.async_limit) {
@@ -149,12 +166,6 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
}, },
}; };
self.* = .{
.interface = interface,
.runtimes_allocator = runtimes_allocator_arena,
.stages = std.EnumMap(Stages, Shader).init(.{}),
};
if (info.p_stages) |stages| { if (info.p_stages) |stages| {
for (stages[0..], 0..info.stage_count) |stage, _| { for (stages[0..], 0..info.stage_count) |stage, _| {
var shader: Shader = undefined; var shader: Shader = undefined;
@@ -164,10 +175,11 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
soft_module.ref(); soft_module.ref();
shader.module = soft_module; shader.module = soft_module;
const runtimes = runtimes_allocator.alloc(spv.Runtime, runtimes_count) catch return VkError.OutOfHostMemory; const runtimes = runtimes_allocator.alloc(Runtime, runtimes_count) catch return VkError.OutOfHostMemory;
for (runtimes) |*runtime| { for (runtimes) |*runtime| {
runtime.* = spv.Runtime.init( runtime.mutex = .init;
runtime.rt = spv.Runtime.init(
runtimes_allocator, runtimes_allocator,
&soft_module.module, &soft_module.module,
.{ .{
@@ -175,6 +187,8 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
.readImageInt4 = readImageInt4, .readImageInt4 = readImageInt4,
.writeImageFloat4 = writeImageFloat4, .writeImageFloat4 = writeImageFloat4,
.writeImageInt4 = writeImageInt4, .writeImageInt4 = writeImageInt4,
.sampleImageFloat4 = sampleImageFloat4,
.queryImageSize = queryImageSize,
}, },
) catch |err| { ) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)}); std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
@@ -184,7 +198,7 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
if (specialization.p_map_entries) |map| { if (specialization.p_map_entries) |map| {
const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size]; const data: []const u8 = @as([*]const u8, @ptrCast(@alignCast(specialization.p_data)))[0..specialization.data_size];
for (map[0..], 0..specialization.map_entry_count) |entry, _| { for (map[0..], 0..specialization.map_entry_count) |entry, _| {
runtime.addSpecializationInfo(runtimes_allocator, .{ runtime.rt.addSpecializationInfo(runtimes_allocator, .{
.id = @intCast(entry.constant_id), .id = @intCast(entry.constant_id),
.offset = @intCast(entry.offset), .offset = @intCast(entry.offset),
.size = @intCast(entry.size), .size = @intCast(entry.size),
@@ -228,30 +242,45 @@ pub fn createGraphics(device: *base.Device, allocator: std.mem.Allocator, cache:
pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void { pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface)); const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const soft_device: *SoftDevice = @alignCast(@fieldParentPtr("interface", interface.owner));
const device_allocator = soft_device.device_allocator.allocator();
var it = self.stages.iterator(); var it = self.stages.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
entry.value.module.unref(allocator); entry.value.module.unref(allocator);
for (entry.value.runtimes) |*runtime| {
runtime.rt.function_stack.clearAndFree(device_allocator); // Hacky to avoid leaks
}
} }
self.runtimes_allocator.deinit(); self.runtimes_allocator.deinit();
allocator.destroy(self); allocator.destroy(self);
} }
fn readImageFloat4(context: *anyopaque, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(f32) { fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); var pixel = zm.f32x4s(0.0);
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); if (dim == .Buffer) {
const pixel = image.readFloat4( const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
.{ const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
.x = x, const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
.y = y, pixel = blitter.readFloat4(map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
.z = z, } else {
}, const image_view: *SoftImageView = @ptrCast(@alignCast(context));
.{ const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
.aspect_mask = image_view.interface.subresource_range.aspect_mask, const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
.mip_level = image_view.interface.subresource_range.base_mip_level, pixel = image.readFloat4(
.array_layer = image_view.interface.subresource_range.base_array_layer, .{
}, .x = x,
image_view.interface.format, .y = y,
) catch return SpvRuntimeError.Unknown; .z = if (dim == .Cube) 0 else z,
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer + cube_face,
},
image_view.interface.format,
) catch return SpvRuntimeError.Unknown;
}
return .{ return .{
.x = pixel[0], .x = pixel[0],
.y = pixel[1], .y = pixel[1],
@@ -260,22 +289,31 @@ fn readImageFloat4(context: *anyopaque, x: i32, y: i32, z: i32) SpvRuntimeError!
}; };
} }
fn readImageInt4(context: *anyopaque, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(u32) { fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) SpvRuntimeError!spv.Runtime.Vec4(u32) {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); var pixel = @Vector(4, u32){ 0, 0, 0, 0 };
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); if (dim == .Buffer) {
const pixel = image.readInt4( const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
.{ const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
.x = x, const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
.y = y, pixel = blitter.readInt4(map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
.z = z, } else {
}, const image_view: *SoftImageView = @ptrCast(@alignCast(context));
.{ const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
.aspect_mask = image_view.interface.subresource_range.aspect_mask, const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
.mip_level = image_view.interface.subresource_range.base_mip_level, pixel = image.readInt4(
.array_layer = image_view.interface.subresource_range.base_array_layer, .{
}, .x = x,
image_view.interface.format, .y = y,
) catch return SpvRuntimeError.Unknown; .z = if (dim == .Cube) 0 else z,
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer + cube_face,
},
image_view.interface.format,
) catch return SpvRuntimeError.Unknown;
}
return .{ return .{
.x = pixel[0], .x = pixel[0],
.y = pixel[1], .y = pixel[1],
@@ -284,40 +322,290 @@ fn readImageInt4(context: *anyopaque, x: i32, y: i32, z: i32) SpvRuntimeError!sp
}; };
} }
fn writeImageFloat4(context: *anyopaque, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(f32)) SpvRuntimeError!void { fn writeImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(f32)) SpvRuntimeError!void {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const vec_pixel = zm.f32x4(pixel.x, pixel.y, pixel.z, pixel.w);
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); if (dim == .Buffer) {
image.writeFloat4( const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
.{ const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
.x = x, const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
.y = y, blitter.writeFloat4(vec_pixel, map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
.z = z, } else {
}, const image_view: *SoftImageView = @ptrCast(@alignCast(context));
.{ const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
.aspect_mask = image_view.interface.subresource_range.aspect_mask, const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
.mip_level = image_view.interface.subresource_range.base_mip_level, image.writeFloat4(
.array_layer = image_view.interface.subresource_range.base_array_layer, .{
}, .x = x,
image_view.interface.format, .y = y,
.{ pixel.x, pixel.y, pixel.z, pixel.w }, .z = if (dim == .Cube) 0 else z,
) catch return SpvRuntimeError.Unknown; },
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer + cube_face,
},
image_view.interface.format,
vec_pixel,
) catch return SpvRuntimeError.Unknown;
}
} }
fn writeImageInt4(context: *anyopaque, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(u32)) SpvRuntimeError!void { fn writeImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: spv.Runtime.Vec4(u32)) SpvRuntimeError!void {
const image_view: *SoftImageView = @ptrCast(@alignCast(context)); const vec_pixel = @Vector(4, u32){ pixel.x, pixel.y, pixel.z, pixel.w };
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image)); if (dim == .Buffer) {
image.writeInt4( const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
blitter.writeInt4(vec_pixel, map[(@as(usize, @intCast(x)) * base.format.texelSize(buffer_view.interface.format))..], buffer_view.interface.format);
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
image.writeInt4(
.{
.x = x,
.y = y,
.z = if (dim == .Cube) 0 else z,
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer + cube_face,
},
image_view.interface.format,
vec_pixel,
) catch return SpvRuntimeError.Unknown;
}
}
const CubeCoordinate = struct {
face: u32,
u: f32,
v: f32,
};
fn resolveCubeCoordinate(x: f32, y: f32, z: f32) CubeCoordinate {
const ax = @abs(x);
const ay = @abs(y);
const az = @abs(z);
var face: u32 = 0;
var sc: f32 = 0.0;
var tc: f32 = 0.0;
var ma: f32 = 1.0;
if (ax >= ay and ax >= az) {
ma = ax;
if (x >= 0.0) {
face = 0;
sc = -z;
tc = -y;
} else {
face = 1;
sc = z;
tc = -y;
}
} else if (ay >= ax and ay >= az) {
ma = ay;
if (y >= 0.0) {
face = 2;
sc = x;
tc = z;
} else {
face = 3;
sc = x;
tc = -z;
}
} else {
ma = az;
if (z >= 0.0) {
face = 4;
sc = x;
tc = -y;
} else {
face = 5;
sc = -x;
tc = -y;
}
}
const inv_ma = if (ma == 0.0) 0.0 else 1.0 / ma;
return .{
.face = face,
.u = (sc * inv_ma + 1.0) * 0.5,
.v = (tc * inv_ma + 1.0) * 0.5,
};
}
fn cubeDirection(face: u32, u: f32, v: f32) struct { x: f32, y: f32, z: f32 } {
const sc = u * 2.0 - 1.0;
const tc = v * 2.0 - 1.0;
return switch (face) {
0 => .{ .x = 1.0, .y = -tc, .z = -sc },
1 => .{ .x = -1.0, .y = -tc, .z = sc },
2 => .{ .x = sc, .y = 1.0, .z = tc },
3 => .{ .x = sc, .y = -1.0, .z = -tc },
4 => .{ .x = sc, .y = -tc, .z = 1.0 },
5 => .{ .x = -sc, .y = -tc, .z = -1.0 },
else => .{ .x = 0.0, .y = 0.0, .z = 0.0 },
};
}
fn readSampledFloat4(
image: *SoftImage,
image_view: *SoftImageView,
dim: spv.SpvDim,
coord: CubeCoordinate,
ix: i32,
iy: i32,
) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
const texel = if (dim == .Cube) blk: {
const dir = cubeDirection(
coord.face,
(@as(f32, @floatFromInt(ix)) + 0.5) / width_f,
(@as(f32, @floatFromInt(iy)) + 0.5) / height_f,
);
break :blk resolveCubeCoordinate(dir.x, dir.y, dir.z);
} else coord;
const result = try image.readFloat4(
.{ .{
.x = x, .x = if (dim == .Cube)
.y = y, std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, image.interface.extent.width - 1)
.z = z, else
std.math.clamp(ix, 0, image.interface.extent.width - 1),
.y = if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, image.interface.extent.height - 1)
else
std.math.clamp(iy, 0, image.interface.extent.height - 1),
.z = 0,
}, },
.{ .{
.aspect_mask = image_view.interface.subresource_range.aspect_mask, .aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level, .mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer, .array_layer = image_view.interface.subresource_range.base_array_layer + texel.face,
}, },
image_view.interface.format, image_view.interface.format,
.{ pixel.x, pixel.y, pixel.z, pixel.w }, );
) catch return SpvRuntimeError.Unknown; return result;
}
fn sampleNearestFloat4(image: *SoftImage, image_view: *SoftImageView, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
return readSampledFloat4(
image,
image_view,
dim,
coord,
@intFromFloat(coord.u * width_f),
@intFromFloat(coord.v * height_f),
);
}
fn sampleLinearFloat4(image: *SoftImage, image_view: *SoftImageView, dim: spv.SpvDim, coord: CubeCoordinate) VkError!zm.F32x4 {
const width_f: f32 = @floatFromInt(image.interface.extent.width);
const height_f: f32 = @floatFromInt(image.interface.extent.height);
const x = coord.u * width_f - 0.5;
const y = coord.v * height_f - 0.5;
const x0: i32 = @intFromFloat(@floor(x));
const y0: i32 = @intFromFloat(@floor(y));
const x1 = x0 + 1;
const y1 = y0 + 1;
const wx = x - @as(f32, @floatFromInt(x0));
const wy = y - @as(f32, @floatFromInt(y0));
const p00 = try readSampledFloat4(image, image_view, dim, coord, x0, y0);
const p10 = try readSampledFloat4(image, image_view, dim, coord, x1, y0);
const p01 = try readSampledFloat4(image, image_view, dim, coord, x0, y1);
const p11 = try readSampledFloat4(image, image_view, dim, coord, x1, y1);
const row0 = p00 * zm.f32x4s(1.0 - wx) + p10 * zm.f32x4s(wx);
const row1 = p01 * zm.f32x4s(1.0 - wx) + p11 * zm.f32x4s(wx);
return row0 * zm.f32x4s(1.0 - wy) + row1 * zm.f32x4s(wy);
}
fn sampleImageFloat4(context: *anyopaque, context2: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32) SpvRuntimeError!spv.Runtime.Vec4(f32) {
var pixel = zm.f32x4s(0.0);
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const buffer: *SoftBuffer = @alignCast(@fieldParentPtr("interface", buffer_view.interface.buffer));
const map = buffer.mapAsSliceWithOffset(u8, buffer_view.interface.offset, buffer_view.interface.range) catch return SpvRuntimeError.Unknown;
_ = map;
} else {
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const sampler: *SoftSampler = @ptrCast(@alignCast(context2));
if (dim == .Cube) {
const coord = resolveCubeCoordinate(x, y, z);
pixel = switch (sampler.interface.mag_filter) {
.linear => sampleLinearFloat4(image, image_view, dim, coord),
else => sampleNearestFloat4(image, image_view, dim, coord),
} catch return SpvRuntimeError.Unknown;
} else {
pixel = image.readFloat4(
.{
.x = std.math.clamp(@as(i32, @intFromFloat(x * @as(f32, @floatFromInt(image.interface.extent.width)))), 0, image.interface.extent.width - 1),
.y = std.math.clamp(@as(i32, @intFromFloat(y * @as(f32, @floatFromInt(image.interface.extent.height)))), 0, image.interface.extent.height - 1),
.z = std.math.clamp(@as(i32, @intFromFloat(z * @as(f32, @floatFromInt(image.interface.extent.depth)))), 0, image.interface.extent.depth - 1),
},
.{
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
.mip_level = image_view.interface.subresource_range.base_mip_level,
.array_layer = image_view.interface.subresource_range.base_array_layer,
},
image_view.interface.format,
) catch return SpvRuntimeError.Unknown;
}
}
return .{
.x = pixel[0],
.y = pixel[1],
.z = pixel[2],
.w = pixel[3],
};
}
fn queryImageSize(context: *anyopaque, dim: spv.SpvDim, arrayed: bool) SpvRuntimeError!spv.Runtime.Vec4(u32) {
if (dim == .Buffer) {
const buffer_view: *SoftBufferView = @ptrCast(@alignCast(context));
const range = if (buffer_view.interface.range == vk.WHOLE_SIZE)
buffer_view.interface.buffer.size - buffer_view.interface.offset
else
buffer_view.interface.range;
return .{
.x = @intCast(@divTrunc(range, base.format.texelSize(buffer_view.interface.format))),
.y = 0,
.z = 0,
.w = 0,
};
}
const image_view: *SoftImageView = @ptrCast(@alignCast(context));
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
const extent = image.getMipLevelExtent(image_view.interface.subresource_range.base_mip_level);
const layers = if (image_view.interface.subresource_range.layer_count == vk.REMAINING_ARRAY_LAYERS)
image.interface.array_layers - image_view.interface.subresource_range.base_array_layer
else
image_view.interface.subresource_range.layer_count;
return switch (dim) {
.@"1D" => if (arrayed)
.{ .x = extent.width, .y = layers, .z = 0, .w = 0 }
else
.{ .x = extent.width, .y = 0, .z = 0, .w = 0 },
.@"2D", .Cube, .Rect => if (arrayed)
.{ .x = extent.width, .y = extent.height, .z = layers, .w = 0 }
else
.{ .x = extent.width, .y = extent.height, .z = 0, .w = 0 },
.@"3D" => .{ .x = extent.width, .y = extent.height, .z = extent.depth, .w = 0 },
else => .{ .x = extent.width, .y = extent.height, .z = layers, .w = 0 },
};
} }
+1 -2
View File
@@ -95,8 +95,7 @@ fn taskRunner(self: *Self, info: Interface.SubmitInfo, p_fence: ?*base.Fence, ru
} }
var execution_device: ExecutionDevice = undefined; var execution_device: ExecutionDevice = undefined;
execution_device.init(soft_device); execution_device.setup(soft_device);
defer execution_device.deinit();
for (info.command_buffers.items) |command_buffer| { for (info.command_buffers.items) |command_buffer| {
const soft_command_buffer: *SoftCommandBuffer = @alignCast(@fieldParentPtr("interface", command_buffer)); const soft_command_buffer: *SoftCommandBuffer = @alignCast(@fieldParentPtr("interface", command_buffer));
+4 -2
View File
@@ -47,8 +47,10 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
spv.Module.ModuleError.OutOfMemory => return VkError.OutOfHostMemory, spv.Module.ModuleError.OutOfMemory => return VkError.OutOfHostMemory,
else => { else => {
std.log.scoped(.@"SPIR-V module").err("module creation catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"SPIR-V module").err("module creation catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (comptime base.config.logs == .verbose) {
std.debug.dumpErrorReturnTrace(trace); if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
} }
return VkError.ValidationFailed; return VkError.ValidationFailed;
}, },
-1
View File
@@ -1 +0,0 @@
#include <cpuinfo.h>
+77
View File
@@ -0,0 +1,77 @@
const std = @import("std");
const base = @import("base");
const Self = @This();
const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
child_allocator: std.mem.Allocator,
bound: usize,
total_bytes_allocated: std.atomic.Value(usize),
peak_concurrent_bytes_allocated: std.atomic.Value(usize),
current_bytes_allocated: std.atomic.Value(usize),
pub fn init(child_allocator: Allocator, bound: usize) Self {
return .{
.child_allocator = child_allocator,
.bound = bound,
.total_bytes_allocated = std.atomic.Value(usize).init(0),
.current_bytes_allocated = std.atomic.Value(usize).init(0),
.peak_concurrent_bytes_allocated = std.atomic.Value(usize).init(0),
};
}
pub fn allocator(self: *const Self) Allocator {
return .{
.ptr = @ptrCast(@constCast(self)), // Ugly const cast for convenience
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
}
pub inline fn queryFootprint(self: *Self) usize {
return self.total_bytes_allocated.load(.monotonic);
}
pub inline fn queryPeakFootprint(self: *Self) usize {
return self.peak_concurrent_bytes_allocated.load(.monotonic);
}
fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(context));
if (self.current_bytes_allocated.fetchAdd(len, .monotonic) >= self.bound)
return null;
_ = self.total_bytes_allocated.fetchAdd(len, .monotonic);
if (self.current_bytes_allocated.load(.monotonic) > self.peak_concurrent_bytes_allocated.load(.monotonic))
self.peak_concurrent_bytes_allocated.store(self.current_bytes_allocated.load(.monotonic), .monotonic);
return self.child_allocator.rawAlloc(len, alignment, ret_addr);
}
fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {
const self: *Self = @ptrCast(@alignCast(context));
_ = self.current_bytes_allocated.fetchSub(ptr.len, .monotonic);
if (self.current_bytes_allocated.fetchAdd(new_len, .monotonic) >= self.bound)
return false;
_ = self.total_bytes_allocated.fetchAdd(new_len, .monotonic);
return self.child_allocator.rawResize(ptr, alignment, new_len, ret_addr);
}
fn remap(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(context));
_ = self.current_bytes_allocated.fetchSub(ptr.len, .monotonic);
if (self.current_bytes_allocated.fetchAdd(new_len, .monotonic) >= self.bound)
return null;
_ = self.total_bytes_allocated.fetchAdd(new_len, .monotonic);
return self.child_allocator.rawRemap(ptr, alignment, new_len, ret_addr);
}
fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, ret_addr: usize) void {
const self: *Self = @ptrCast(@alignCast(context));
_ = self.current_bytes_allocated.fetchSub(ptr.len, .monotonic);
return self.child_allocator.rawFree(ptr, alignment, ret_addr);
}
-73
View File
@@ -1,73 +0,0 @@
const std = @import("std");
const base = @import("base");
const Self = @This();
const Allocator = std.mem.Allocator;
const Alignment = std.mem.Alignment;
mutex: base.SpinMutex,
arena: std.heap.ArenaAllocator,
bound: usize,
pub fn init(child_allocator: Allocator, bound: usize) Self {
return .{
.mutex = .{},
.arena = .init(child_allocator),
.bound = bound,
};
}
pub fn deinit(self: *Self) void {
self.arena.deinit();
}
pub fn allocator(self: *const Self) Allocator {
return .{
.ptr = @ptrCast(@constCast(self)), // Ugly const cast for convenience
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
}
pub inline fn queryCapacity(self: *Self) usize {
return self.arena.queryCapacity();
}
fn alloc(context: *anyopaque, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(context));
self.mutex.lock();
defer self.mutex.unlock();
if (self.arena.queryCapacity() >= self.bound)
return null;
return self.arena.allocator().rawAlloc(len, alignment, ret_addr);
}
fn resize(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {
const self: *Self = @ptrCast(@alignCast(context));
self.mutex.lock();
defer self.mutex.unlock();
if (self.arena.queryCapacity() >= self.bound)
return false;
return self.arena.allocator().rawResize(ptr, alignment, new_len, ret_addr);
}
fn remap(context: *anyopaque, ptr: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(context));
self.mutex.lock();
defer self.mutex.unlock();
if (self.arena.queryCapacity() >= self.bound)
return null;
return self.arena.allocator().rawRemap(ptr, alignment, new_len, ret_addr);
}
fn free(context: *anyopaque, ptr: []u8, alignment: Alignment, ret_addr: usize) void {
const self: *Self = @ptrCast(@alignCast(context));
self.mutex.lock();
defer self.mutex.unlock();
return self.arena.allocator().rawFree(ptr, alignment, ret_addr);
}
+99 -48
View File
@@ -4,7 +4,8 @@ const base = @import("base");
const spv = @import("spv"); const spv = @import("spv");
const lib = @import("../lib.zig"); const lib = @import("../lib.zig");
const PipelineState = @import("Device.zig").PipelineState; const ExecutionDevice = @import("Device.zig");
const PipelineState = ExecutionDevice.PipelineState;
const SoftDevice = @import("../SoftDevice.zig"); const SoftDevice = @import("../SoftDevice.zig");
const SoftPipeline = @import("../SoftPipeline.zig"); const SoftPipeline = @import("../SoftPipeline.zig");
@@ -45,10 +46,6 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
}; };
} }
pub fn deinit(self: *Self) void {
_ = self;
}
pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void { pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
const group_count: usize = @intCast(group_count_x * group_count_y * group_count_z); const group_count: usize = @intCast(group_count_x * group_count_y * group_count_z);
@@ -61,6 +58,14 @@ pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count
self.invocation_index.store(0, .monotonic); self.invocation_index.store(0, .monotonic);
const io = self.device.interface.io();
const timer = std.Io.Timestamp.now(io, .real);
defer if (comptime base.config.logs != .none) {
const duration = timer.untilNow(io, .real);
const ms: f32 = @floatFromInt(duration.toMicroseconds());
std.log.scoped(.ComputeDispatcher).debug("Compute dispatch took {}ms", .{ms / 1000});
};
var wg: std.Io.Group = .init; var wg: std.Io.Group = .init;
for (0..@min(self.batch_size, group_count)) |batch_id| { for (0..@min(self.batch_size, group_count)) |batch_id| {
const run_data: RunData = .{ const run_data: RunData = .{
@@ -85,8 +90,10 @@ pub fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count
fn runWrapper(data: RunData) void { fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| { @call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (comptime base.config.logs == .verbose) {
std.debug.dumpErrorReturnTrace(trace); if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
} }
}; };
} }
@@ -96,11 +103,31 @@ inline fn run(data: RunData) !void {
const io = data.self.device.interface.io(); const io = data.self.device.interface.io();
const shader = data.pipeline.stages.getPtrAssertContains(.compute); const shader = data.pipeline.stages.getPtrAssertContains(.compute);
const rt = &shader.runtimes[data.batch_id]; const rt = &shader.runtimes[data.batch_id].rt;
const entry = try rt.getEntryPointByName(shader.entry); const entry = try rt.getEntryPointByName(shader.entry);
const uses_control_barrier = hasControlBarrier(rt.mod.code);
try data.self.writeDescriptorSets(rt); var barrier_runtimes: []spv.Runtime = &.{};
var barrier_statuses: []spv.Runtime.EntryPointStatus = &.{};
if (uses_control_barrier) {
barrier_runtimes = try allocator.alloc(spv.Runtime, data.invocations_per_workgroup);
barrier_statuses = try allocator.alloc(spv.Runtime.EntryPointStatus, data.invocations_per_workgroup);
for (barrier_runtimes) |*barrier_rt| {
barrier_rt.* = try spv.Runtime.init(allocator, rt.mod, rt.image_api);
try barrier_rt.copySpecializationConstantsFrom(allocator, rt);
}
}
defer {
for (barrier_runtimes) |*barrier_rt| {
barrier_rt.deinit(allocator);
}
allocator.free(barrier_runtimes);
allocator.free(barrier_statuses);
}
if (!uses_control_barrier)
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
var group_index: usize = data.batch_id; var group_index: usize = data.batch_id;
while (group_index < data.group_count) : (group_index += data.self.batch_size) { while (group_index < data.group_count) : (group_index += data.self.batch_size) {
@@ -114,15 +141,23 @@ inline fn run(data: RunData) !void {
modulo -= group_y * data.group_count_x; modulo -= group_y * data.group_count_x;
const group_x = modulo; const group_x = modulo;
try setupWorkgroupBuiltins(data.self, rt, .{ const group_count_vec = @Vector(3, u32){
@as(u32, @intCast(data.group_count_x)), @as(u32, @intCast(data.group_count_x)),
@as(u32, @intCast(data.group_count_y)), @as(u32, @intCast(data.group_count_y)),
@as(u32, @intCast(data.group_count_z)), @as(u32, @intCast(data.group_count_z)),
}, .{ };
const group_id_vec = @Vector(3, u32){
@as(u32, @intCast(group_x)), @as(u32, @intCast(group_x)),
@as(u32, @intCast(group_y)), @as(u32, @intCast(group_y)),
@as(u32, @intCast(group_z)), @as(u32, @intCast(group_z)),
}); };
if (uses_control_barrier) {
try runBarrierWorkgroup(data, barrier_runtimes, barrier_statuses, entry, group_count_vec, group_id_vec);
continue;
}
try setupWorkgroupBuiltins(data.self, rt, group_count_vec, group_id_vec);
for (0..data.invocations_per_workgroup) |i| { for (0..data.invocations_per_workgroup) |i| {
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic); const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
@@ -156,6 +191,58 @@ inline fn run(data: RunData) !void {
} }
} }
fn runBarrierWorkgroup(
data: RunData,
runtimes: []spv.Runtime,
statuses: []spv.Runtime.EntryPointStatus,
entry: spv.SpvWord,
group_count: @Vector(3, u32),
group_id: @Vector(3, u32),
) !void {
const allocator = data.self.device.device_allocator.allocator();
for (runtimes, 0..) |*rt, i| {
try ExecutionDevice.writeDescriptorSets(data.self.state, rt);
try setupWorkgroupBuiltins(data.self, rt, group_count, group_id);
try setupSubgroupBuiltins(data.self, rt, group_id, i);
statuses[i] = try rt.beginEntryPoint(allocator, entry);
try rt.flushDescriptorSets(allocator);
}
while (true) {
var pending = false;
for (statuses) |status| {
if (status == .barrier) {
pending = true;
break;
}
}
if (!pending)
break;
for (runtimes, 0..) |*rt, i| {
if (statuses[i] == .completed)
continue;
statuses[i] = try rt.continueEntryPoint(allocator);
try rt.flushDescriptorSets(allocator);
}
}
}
/// TODO: Move this in the SPIR-V Interpreter
fn hasControlBarrier(code: []const spv.SpvWord) bool {
var i: usize = 5;
while (i < code.len) {
const opcode_data = code[i];
const word_count = (opcode_data & (~spv.spv.SpvOpCodeMask)) >> spv.spv.SpvWordCountShift;
const opcode: spv.spv.SpvOp = @enumFromInt(opcode_data & spv.spv.SpvOpCodeMask);
if (opcode == .ControlBarrier)
return true;
i += @max(word_count, 1);
}
return false;
}
inline fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Runtime, is_early: bool) !void { inline fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Runtime, is_early: bool) !void {
@branchHint(.cold); @branchHint(.cold);
const file = try std.Io.Dir.cwd().createFile( const file = try std.Io.Dir.cwd().createFile(
@@ -169,42 +256,6 @@ inline fn dumpResultsTable(allocator: std.mem.Allocator, io: std.Io, rt: *spv.Ru
try rt.dumpResultsTable(allocator, &writer.interface); try rt.dumpResultsTable(allocator, &writer.interface);
} }
fn writeDescriptorSets(self: *Self, rt: *spv.Runtime) !void {
sets: for (self.state.sets[0..], 0..) |set, set_index| {
if (set == null)
continue :sets;
bindings: for (set.?.descriptors[0..], 0..) |binding, binding_index| {
switch (binding) {
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
if (buffer_data.object) |buffer| {
const memory = if (buffer.interface.memory) |memory| memory else continue :bindings;
const map: []u8 = @as([*]u8, @ptrCast(try memory.map(buffer_data.offset, buffer_data.size)))[0..buffer_data.size];
try rt.writeDescriptorSet(
map,
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
}
},
.image => |image_data_array| for (image_data_array, 0..) |image_data, descriptor_index| {
if (image_data.object) |image_view| {
const addr: usize = @intFromPtr(image_view);
try rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
}
},
else => {},
}
}
}
}
fn setupWorkgroupBuiltins( fn setupWorkgroupBuiltins(
self: *Self, self: *Self,
rt: *spv.Runtime, rt: *spv.Runtime,
+76 -4
View File
@@ -2,6 +2,7 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const lib = @import("../lib.zig"); const lib = @import("../lib.zig");
const spv = @import("spv");
const SoftDescriptorSet = @import("../SoftDescriptorSet.zig"); const SoftDescriptorSet = @import("../SoftDescriptorSet.zig");
const SoftDevice = @import("../SoftDevice.zig"); const SoftDevice = @import("../SoftDevice.zig");
@@ -22,6 +23,7 @@ pub const COMPUTE_PIPELINE_STATE = 1;
pub const PipelineState = struct { pub const PipelineState = struct {
pipeline: ?*SoftPipeline, pipeline: ?*SoftPipeline,
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet, sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet,
push_constant_blob: [lib.PUSH_CONSTANT_SIZE]u8,
data: union { data: union {
compute: struct {}, compute: struct {},
graphics: struct { graphics: struct {
@@ -38,11 +40,12 @@ pipeline_states: [2]PipelineState,
/// Initializating an execution device and /// Initializating an execution device and
/// not creating one to avoid dangling pointers /// not creating one to avoid dangling pointers
pub fn init(self: *Self, device: *SoftDevice) void { pub fn setup(self: *Self, device: *SoftDevice) void {
for (self.pipeline_states[0..], 0..) |*state, i| { for (self.pipeline_states[0..], 0..) |*state, i| {
state.* = .{ state.* = .{
.pipeline = null, .pipeline = null,
.sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS, .sets = [_]?*SoftDescriptorSet{null} ** base.VULKAN_MAX_DESCRIPTOR_SETS,
.push_constant_blob = @splat(0),
.data = switch (i) { .data = switch (i) {
GRAPHICS_PIPELINE_STATE => .{ GRAPHICS_PIPELINE_STATE => .{
.graphics = .{ .graphics = .{
@@ -59,7 +62,76 @@ pub fn init(self: *Self, device: *SoftDevice) void {
self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)]); self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)]);
} }
pub fn deinit(self: *Self) void { pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
self.compute.deinit(); sets: for (state.sets[0..], 0..) |set, set_index| {
self.renderer.deinit(); if (set == null)
continue :sets;
bindings: for (set.?.descriptors[0..], 0..) |binding, binding_index| {
switch (binding) {
.buffer => |buffer_data_array| for (buffer_data_array, 0..) |buffer_data, descriptor_index| {
if (buffer_data.object) |buffer| {
const map = buffer.mapAsSliceWithAddedOffset(u8, buffer_data.offset, buffer_data.size) catch continue :bindings;
try rt.writeDescriptorSet(
map,
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
}
},
.image => |image_data_array| for (image_data_array, 0..) |image_data, descriptor_index| {
if (image_data.object) |image_view| {
const addr: usize = @intFromPtr(image_view);
try rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
}
},
.texel_buffer => |texel_data_array| for (texel_data_array, 0..) |texel_data, descriptor_index| {
if (texel_data.object) |buffer_view| {
const addr: usize = @intFromPtr(buffer_view);
try rt.writeDescriptorSet(
std.mem.asBytes(&addr),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
}
},
.texture => |texture_data_array| for (texture_data_array, 0..) |texture_data, descriptor_index| {
const SampledImage = packed struct {
image: usize,
sampler: usize,
};
var data: SampledImage = undefined;
if (texture_data.view) |image_view| {
const addr: usize = @intFromPtr(image_view);
data.image = addr;
}
if (texture_data.sampler) |sampler| {
const addr: usize = @intFromPtr(sampler);
data.sampler = addr;
}
try rt.writeDescriptorSet(
std.mem.asBytes(&data),
@as(u32, @intCast(set_index)),
@as(u32, @intCast(binding_index)),
@as(u32, @intCast(descriptor_index)),
);
},
else => {},
}
}
}
} }
+139 -251
View File
@@ -2,28 +2,26 @@ const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const zm = base.zm; const zm = base.zm;
const lib = @import("../lib.zig");
const spv = @import("spv"); const spv = @import("spv");
pub const F32x4 = zm.F32x4; const ExecutionDevice = @import("Device.zig");
const PipelineState = ExecutionDevice.PipelineState;
const PipelineState = @import("Device.zig").PipelineState; const BoundedAllocator = @import("BoundedAllocator.zig");
const BoundedArenaAllocator = @import("BoundedArenaAllocator.zig");
const SoftBuffer = @import("../SoftBuffer.zig"); const SoftBuffer = @import("../SoftBuffer.zig");
const SoftDescriptorSet = @import("../SoftDescriptorSet.zig"); const SoftDescriptorSet = @import("../SoftDescriptorSet.zig");
const SoftDevice = @import("../SoftDevice.zig"); const SoftDevice = @import("../SoftDevice.zig");
const SoftFramebuffer = @import("../SoftFramebuffer.zig"); const SoftFramebuffer = @import("../SoftFramebuffer.zig");
const SoftImage = @import("../SoftImage.zig");
const SoftPipeline = @import("../SoftPipeline.zig"); const SoftPipeline = @import("../SoftPipeline.zig");
const SoftRenderPass = @import("../SoftRenderPass.zig"); const SoftRenderPass = @import("../SoftRenderPass.zig");
const blitter = @import("blitter.zig"); const blitter = @import("blitter.zig");
const rasterizer = @import("rasterizer.zig"); const rasterizer = @import("rasterizer.zig");
const vertex_dispatcher = @import("vertex_dispatcher.zig"); const vertex_dispatcher = @import("vertex_dispatcher.zig");
const fragment_dispatcher = @import("fragment_dispatcher.zig"); const clip = @import("clip.zig");
const VkError = base.VkError; const VkError = base.VkError;
const F32x4 = zm.F32x4;
const Self = @This(); const Self = @This();
@@ -43,7 +41,7 @@ pub const IndexBuffer = struct {
pub const DynamicState = struct { pub const DynamicState = struct {
viewports: ?[]const vk.Viewport, viewports: ?[]const vk.Viewport,
scissor: ?[]vk.Rect2D, scissor: ?[]const vk.Rect2D,
line_width: ?f32, line_width: ?f32,
}; };
@@ -55,20 +53,42 @@ pub const Vertex = struct {
}, },
}; };
pub const Fragment = struct {
position: F32x4,
color: F32x4,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][]u8,
};
pub const DrawCall = struct { pub const DrawCall = struct {
renderer: *Self,
vertices: []Vertex, vertices: []Vertex,
fragments: []Fragment,
pub fn init(allocator: std.mem.Allocator, vertex_count: usize, instance_count: usize) VkError!@This() { viewport: vk.Viewport,
scissor: vk.Rect2D,
color_attachments: []*base.ImageView,
depth_attachment: ?*base.ImageView,
render_pass: *SoftRenderPass,
framebuffer: *SoftFramebuffer,
rasterizer_wait_group: std.Io.Group,
stats: struct {
polygons_drawn: usize,
},
fn init(allocator: std.mem.Allocator, vertex_count: usize, instance_count: usize, renderer: *Self) VkError!@This() {
const framebuffer = renderer.framebuffer orelse return VkError.InvalidHandleDrv;
const render_pass = renderer.render_pass orelse return VkError.InvalidHandleDrv;
const self: @This() = .{ const self: @This() = .{
.vertices = allocator.alloc(Vertex, vertex_count * instance_count) catch return VkError.OutOfDeviceMemory, .vertices = allocator.alloc(Vertex, vertex_count * instance_count) catch return VkError.OutOfDeviceMemory,
.fragments = undefined, .renderer = renderer,
.viewport = undefined,
.scissor = undefined,
.color_attachments = framebuffer.interface.attachments[0..],
.depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null,
.render_pass = render_pass,
.framebuffer = framebuffer,
.rasterizer_wait_group = .init,
.stats = .{
.polygons_drawn = 0,
},
}; };
for (self.vertices) |*vertex| { for (self.vertices) |*vertex| {
@@ -77,6 +97,17 @@ pub const DrawCall = struct {
return self; return self;
} }
fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
for (self.vertices) |*vertex| {
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
if (vertex.outputs[location]) |output| {
allocator.free(output.blob);
}
}
}
allocator.free(self.vertices);
}
}; };
device: *SoftDevice, device: *SoftDevice,
@@ -86,6 +117,8 @@ render_pass: ?*SoftRenderPass,
framebuffer: ?*SoftFramebuffer, framebuffer: ?*SoftFramebuffer,
dynamic_state: DynamicState, dynamic_state: DynamicState,
subpass_index: usize,
pub fn init(device: *SoftDevice, state: *PipelineState) Self { pub fn init(device: *SoftDevice, state: *PipelineState) Self {
return .{ return .{
.device = device, .device = device,
@@ -97,74 +130,84 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
.scissor = null, .scissor = null,
.line_width = null, .line_width = null,
}, },
.subpass_index = 0,
}; };
} }
pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void { pub fn draw(self: *Self, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize) VkError!void {
const io = self.device.interface.io(); var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
try self.drawCall(&bounded_allocator, vertex_count, instance_count, first_vertex, first_instance, null);
var arena: BoundedArenaAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
defer arena.deinit();
const allocator = arena.allocator();
var draw_call = try DrawCall.init(allocator, vertex_count, instance_count);
const timer = std.Io.Timestamp.now(io, .real);
defer if (comptime base.config.logs != .none) {
const duration = timer.untilNow(io, .real);
const ms = duration.toMicroseconds();
const memory_footprint = @divTrunc(arena.queryCapacity(), 1000);
const logger = std.log.scoped(.SoftwareRenderer);
if (memory_footprint > 256_000)
logger.warn("Drawcall stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint })
else
logger.debug("Drawcall stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint });
};
self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, null) catch |err| {
std.log.scoped(.@"Vertex stage").err("catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
};
try self.postVertexDraw(allocator, &draw_call);
} }
pub fn drawIndexed(self: *Self, index_count: usize, instance_count: usize, first_index: usize, first_instance: usize, vertex_offset: i32) VkError!void { pub fn drawIndexed(self: *Self, index_count: usize, instance_count: usize, first_index: usize, first_instance: usize, vertex_offset: i32) VkError!void {
const io = self.device.interface.io(); var bounded_allocator: BoundedAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
const allocator = bounded_allocator.allocator();
var arena: BoundedArenaAllocator = .init(self.device.device_allocator.allocator(), @"1GiB");
defer arena.deinit();
const allocator = arena.allocator();
var draw_call = try DrawCall.init(allocator, index_count, instance_count);
const indices = try self.readIndexBuffer(allocator, index_count, first_index, vertex_offset); const indices = try self.readIndexBuffer(allocator, index_count, first_index, vertex_offset);
try self.drawCall(&bounded_allocator, index_count, instance_count, 0, first_instance, indices);
}
fn drawCall(self: *Self, bounded_allocator: *BoundedAllocator, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize, indices: ?[]const i32) VkError!void {
const io = self.device.interface.io();
const allocator = bounded_allocator.allocator();
var draw_call = try DrawCall.init(allocator, vertex_count, instance_count, self);
defer draw_call.deinit(allocator);
const timer = std.Io.Timestamp.now(io, .real); const timer = std.Io.Timestamp.now(io, .real);
defer if (comptime base.config.logs != .none) { defer if (comptime base.config.logs != .none) {
const duration = timer.untilNow(io, .real); const duration = timer.untilNow(io, .real);
const ms = duration.toMicroseconds(); const ms: f32 = @floatFromInt(duration.toMicroseconds());
const memory_footprint = @divTrunc(arena.queryCapacity(), 1000); const memory_footprint = @divTrunc(bounded_allocator.queryFootprint(), 1000);
const peak_memory_footprint = @divTrunc(bounded_allocator.queryPeakFootprint(), 1000);
const fmt =
\\Drawcall stats:
\\> Took {d:.3}ms
\\> Total allocation of {d} KB
\\> Peak concurrent allocation of {d} KB
\\> Total polygons drawn {d}
;
const args = .{
ms / 1000,
memory_footprint,
peak_memory_footprint,
draw_call.stats.polygons_drawn,
};
const logger = std.log.scoped(.SoftwareRenderer); const logger = std.log.scoped(.SoftwareRenderer);
if (memory_footprint > 256_000) if (memory_footprint > 256_000)
logger.warn("Drawcall indexed stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint }) logger.warn(fmt, args)
else else
logger.debug("Drawcall indexed stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint }); logger.debug(fmt, args);
}; };
self.vertexShaderStage(allocator, &draw_call, index_count, instance_count, 0, first_instance, indices) catch |err| { const pipeline = self.state.pipeline orelse return VkError.InvalidPipelineDrv;
const vertex_shader = pipeline.stages.getPtrAssertContains(.vertex);
for (vertex_shader.runtimes[0..]) |*runtime| {
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
}
const fragment_shader = pipeline.stages.getPtrAssertContains(.fragment);
for (fragment_shader.runtimes[0..]) |*runtime| {
ExecutionDevice.writeDescriptorSets(self.state, &runtime.rt) catch return VkError.Unknown;
}
self.vertexShaderStage(allocator, &draw_call, vertex_count, instance_count, first_vertex, first_instance, indices) catch |err| {
std.log.scoped(.@"Vertex stage").err("catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"Vertex stage").err("catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (comptime base.config.logs == .verbose) {
std.debug.dumpErrorReturnTrace(trace); if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
} }
return VkError.Unknown;
}; };
try self.postVertexDraw(allocator, &draw_call); draw_call.viewport = try self.resolveViewport(0);
} draw_call.scissor = try self.resolveScissor(0);
pub fn deinit(self: *Self) void { try rasterizer.processThenFragmentStage(self, allocator, &draw_call);
_ = self;
} }
fn vertexShaderStage(self: *Self, allocator: std.mem.Allocator, draw_call: *DrawCall, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize, indices: ?[]const i32) !void { fn vertexShaderStage(self: *Self, allocator: std.mem.Allocator, draw_call: *DrawCall, vertex_count: usize, instance_count: usize, first_vertex: usize, first_instance: usize, indices: ?[]const i32) !void {
@@ -176,7 +219,6 @@ fn vertexShaderStage(self: *Self, allocator: std.mem.Allocator, draw_call: *Draw
for (0..@min(batch_size, vertex_count)) |batch_id| { for (0..@min(batch_size, vertex_count)) |batch_id| {
const run_data: vertex_dispatcher.RunData = .{ const run_data: vertex_dispatcher.RunData = .{
.allocator = allocator, .allocator = allocator,
.renderer = self,
.pipeline = pipeline, .pipeline = pipeline,
.batch_id = batch_id, .batch_id = batch_id,
.batch_size = batch_size, .batch_size = batch_size,
@@ -194,167 +236,6 @@ fn vertexShaderStage(self: *Self, allocator: std.mem.Allocator, draw_call: *Draw
wg.await(self.device.interface.io()) catch return VkError.DeviceLost; wg.await(self.device.interface.io()) catch return VkError.DeviceLost;
} }
fn postVertexDraw(self: *Self, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void {
const render_target_view: *base.ImageView = (self.framebuffer orelse return).interface.attachments[0];
const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image));
try self.primitiveAssemblyStage(draw_call);
try self.rasterizationStage(allocator, draw_call);
self.fragmentShaderStage(draw_call) catch |err| {
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
};
for (draw_call.fragments) |fragment| {
try render_target.writeFloat4(
.{
.x = @intFromFloat(fragment.position[0]),
.y = @intFromFloat(fragment.position[1]),
.z = 0, // FIXME
},
.{
.aspect_mask = render_target_view.subresource_range.aspect_mask,
.mip_level = render_target_view.subresource_range.base_mip_level,
.array_layer = render_target_view.subresource_range.base_array_layer,
},
render_target_view.format,
fragment.color,
);
}
}
fn primitiveAssemblyStage(self: *Self, draw_call: *DrawCall) VkError!void {
const viewport = blk: {
const pipeline_data = &(self.state.pipeline orelse return VkError.InvalidPipelineDrv).interface.mode.graphics;
if (pipeline_data.dynamic_state.viewport) {
if (self.dynamic_state.viewports) |viewports|
break :blk viewports[0];
}
if (pipeline_data.viewport_state.viewports) |viewports|
break :blk viewports[0];
return VkError.Unknown;
};
for (draw_call.vertices) |*vertex| {
const x = vertex.position[0];
const y = vertex.position[1];
const z = vertex.position[2];
const w = vertex.position[3];
// Perspective division.
const x_ndc = x / w;
const y_ndc = y / w;
const z_ndc = z / w;
const p_x = viewport.width;
const p_y = viewport.height;
const p_z = viewport.max_depth - viewport.min_depth;
const o_x = viewport.x + viewport.width / 2.0;
const o_y = viewport.y + viewport.height / 2.0;
const o_z = viewport.min_depth;
const x_screen = ((p_x / 2.0) * x_ndc) + o_x;
const y_screen = ((p_y / 2.0) * y_ndc) + o_y;
const z_screen = (p_z * z_ndc) + o_z;
vertex.position = zm.f32x4(x_screen, y_screen, z_screen, 1.0);
}
}
fn rasterizationStage(self: *Self, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void {
var fragments: std.ArrayList(Fragment) = .empty;
const pipeline_data = (self.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
const topology = pipeline_data.input_assembly.topology;
switch (topology) {
.triangle_list => for (0..@divTrunc(draw_call.vertices.len, 3)) |triangle_index| {
const first_vertex = triangle_index * 3;
const v0 = &draw_call.vertices[first_vertex + 0];
const v1 = &draw_call.vertices[first_vertex + 1];
const v2 = &draw_call.vertices[first_vertex + 2];
try self.rasterizeTriangle(allocator, &fragments, v0, v1, v2, v0, v1, v2);
},
.triangle_fan => if (draw_call.vertices.len >= 3) {
const v0 = &draw_call.vertices[0];
for (1..(draw_call.vertices.len - 1)) |vertex_index| {
const v1 = &draw_call.vertices[vertex_index];
const v2 = &draw_call.vertices[vertex_index + 1];
try self.rasterizeTriangle(allocator, &fragments, v0, v1, v2, v0, v1, v2);
}
},
.triangle_strip => if (draw_call.vertices.len >= 3) {
for (0..(draw_call.vertices.len - 2)) |vertex_index| {
const v0 = &draw_call.vertices[vertex_index + 0];
const v1 = &draw_call.vertices[vertex_index + 1];
const v2 = &draw_call.vertices[vertex_index + 2];
if ((vertex_index & 1) == 0) {
try self.rasterizeTriangle(allocator, &fragments, v0, v1, v2, v0, v1, v2);
} else {
try self.rasterizeTriangle(allocator, &fragments, v0, v1, v2, v1, v0, v2);
}
}
},
else => base.unsupported("primitive topology {any}", .{topology}),
}
draw_call.fragments = fragments.toOwnedSlice(allocator) catch return VkError.OutOfDeviceMemory;
}
fn rasterizeTriangle(
self: *Self,
allocator: std.mem.Allocator,
fragments: *std.ArrayList(Fragment),
v0: *Vertex,
v1: *Vertex,
v2: *Vertex,
cull_v0: *const Vertex,
cull_v1: *const Vertex,
cull_v2: *const Vertex,
) VkError!void {
if (try self.triangleIsCulled(cull_v0, cull_v1, cull_v2))
return;
const pipeline_data = (self.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
switch (pipeline_data.rasterization.polygon_mode) {
.fill => try rasterizer.drawTriangleFilled(allocator, fragments, v0, v1, v2),
.line => {
try rasterizer.drawLineBresenham(allocator, fragments, v0, v1);
try rasterizer.drawLineBresenham(allocator, fragments, v1, v2);
try rasterizer.drawLineBresenham(allocator, fragments, v2, v0);
},
.point => {},
else => base.unsupported("polygon mode {any}", .{pipeline_data.rasterization.polygon_mode}),
}
}
fn fragmentShaderStage(self: *Self, draw_call: *DrawCall) !void {
const pipeline = self.state.pipeline orelse return;
const batch_size = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
const fragment_count = draw_call.fragments.len;
var wg: std.Io.Group = .init;
for (0..@min(batch_size, fragment_count)) |batch_id| {
const run_data: fragment_dispatcher.RunData = .{
.renderer = self,
.pipeline = pipeline,
.batch_id = batch_id,
.batch_size = batch_size,
.fragment_count = fragment_count,
.draw_call = draw_call,
};
wg.async(self.device.interface.io(), fragment_dispatcher.runWrapper, .{run_data});
}
wg.await(self.device.interface.io()) catch return VkError.DeviceLost;
}
fn readIndexBuffer(self: *Self, allocator: std.mem.Allocator, index_count: usize, first_index: usize, vertex_offset: i32) VkError![]i32 { fn readIndexBuffer(self: *Self, allocator: std.mem.Allocator, index_count: usize, first_index: usize, vertex_offset: i32) VkError![]i32 {
const index_buffer = self.state.data.graphics.index_buffer; const index_buffer = self.state.data.graphics.index_buffer;
const buffer = index_buffer.buffer; const buffer = index_buffer.buffer;
@@ -366,7 +247,7 @@ fn readIndexBuffer(self: *Self, allocator: std.mem.Allocator, index_count: usize
const byte_offset = buffer.interface.offset + index_buffer.offset + (first_index * index_size); const byte_offset = buffer.interface.offset + index_buffer.offset + (first_index * index_size);
const byte_size = index_count * index_size; const byte_size = index_count * index_size;
const index_memory: []const u8 = @as([*]const u8, @ptrCast(@alignCast(try buffer_memory.map(byte_offset, byte_size))))[0..byte_size]; const index_memory: []const u8 = try buffer_memory.map(byte_offset, byte_size);
const indices = allocator.alloc(i32, index_count) catch return VkError.OutOfDeviceMemory; const indices = allocator.alloc(i32, index_count) catch return VkError.OutOfDeviceMemory;
for (indices, 0..) |*index, i| { for (indices, 0..) |*index, i| {
@@ -392,37 +273,44 @@ fn indexTypeSize(index_type: vk.IndexType) ?usize {
}; };
} }
fn triangleArea2(v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) f32 { fn resolveViewport(self: *Self, viewport_index: usize) VkError!vk.Viewport {
const x0 = v0.position[0]; const pipeline_data =
const y0 = v0.position[1]; &(self.state.pipeline orelse return VkError.InvalidPipelineDrv).interface.mode.graphics;
const x1 = v1.position[0];
const y1 = v1.position[1];
const x2 = v2.position[0];
const y2 = v2.position[1];
return ((x1 - x0) * (y2 - y0)) - ((y1 - y0) * (x2 - x0)); if (pipeline_data.dynamic_state.viewport) {
if (self.dynamic_state.viewports) |viewports| {
if (viewport_index < viewports.len)
return viewports[viewport_index];
}
return VkError.Unknown;
}
if (pipeline_data.viewport_state.viewports) |viewports| {
if (viewport_index < viewports.len)
return viewports[viewport_index];
}
return VkError.Unknown;
} }
fn triangleIsCulled(self: *Self, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!bool { fn resolveScissor(self: *Self, scissor_index: usize) VkError!vk.Rect2D {
const pipeline_data = (self.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics; const pipeline_data =
const rasterization = pipeline_data.rasterization; &(self.state.pipeline orelse return VkError.InvalidPipelineDrv).interface.mode.graphics;
const cull_mode = rasterization.cull_mode;
if (!cull_mode.front_bit and !cull_mode.back_bit) if (pipeline_data.dynamic_state.scissor) {
return false; if (self.dynamic_state.scissor) |scissor| {
if (scissor_index < scissor.len)
return scissor[scissor_index];
}
if (cull_mode.front_bit and cull_mode.back_bit) return VkError.Unknown;
return true; }
const area = triangleArea2(v0, v1, v2); if (pipeline_data.viewport_state.scissor) |scissor| {
if (area == 0.0) if (scissor_index < scissor.len)
return true; return scissor[scissor_index];
}
const front_face = switch (rasterization.front_face) { return VkError.Unknown;
.counter_clockwise => area < 0.0,
.clockwise => area > 0.0,
else => return false,
};
return (cull_mode.front_bit and front_face) or (cull_mode.back_bit and !front_face);
} }
+1003 -193
View File
File diff suppressed because it is too large Load Diff
+220
View File
@@ -0,0 +1,220 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const zm = base.zm;
const spv = @import("spv");
pub const F32x4 = zm.F32x4;
const Renderer = @import("Renderer.zig");
const Vertex = Renderer.Vertex;
const VkError = base.VkError;
const ClipPlane = enum {
Left,
Right,
Bottom,
Top,
Near,
Far,
};
const MAX_CLIPPED_POLYGON_VERTICES = 16;
pub const ClippedLine = struct {
v0: Vertex,
v1: Vertex,
};
const ClippedPolygon = struct {
vertices: [MAX_CLIPPED_POLYGON_VERTICES]Vertex = undefined,
len: usize = 0,
fn append(self: *@This(), vertex: Vertex) VkError!void {
if (self.len >= self.vertices.len)
return VkError.OutOfDeviceMemory;
self.vertices[self.len] = vertex;
self.len += 1;
}
};
pub fn clipTriangle(allocator: std.mem.Allocator, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!ClippedPolygon {
var polygon: ClippedPolygon = .{};
try polygon.append(v0.*);
try polygon.append(v1.*);
try polygon.append(v2.*);
const planes = [_]ClipPlane{
.Left,
.Right,
.Bottom,
.Top,
.Near,
.Far,
};
for (planes) |plane| {
polygon = try clipPolygonAgainstPlane(allocator, &polygon, plane);
if (polygon.len < 3)
return polygon;
}
return polygon;
}
pub fn clipLine(allocator: std.mem.Allocator, v0: *const Vertex, v1: *const Vertex) VkError!?ClippedLine {
var line: ClippedLine = .{
.v0 = v0.*,
.v1 = v1.*,
};
const planes = [_]ClipPlane{
.Left,
.Right,
.Bottom,
.Top,
.Near,
.Far,
};
for (planes) |plane| {
const v0_distance = clipDistance(line.v0.position, plane);
const v1_distance = clipDistance(line.v1.position, plane);
const v0_inside = v0_distance >= 0.0;
const v1_inside = v1_distance >= 0.0;
if (!v0_inside and !v1_inside)
return null;
if (v0_inside and v1_inside)
continue;
const t = v0_distance / (v0_distance - v1_distance);
const clipped_vertex = try interpolateVertexForClipping(allocator, &line.v0, &line.v1, t);
if (v0_inside) {
line.v1 = clipped_vertex;
} else {
line.v0 = clipped_vertex;
}
}
return line;
}
pub fn viewportTransformVertex(viewport: vk.Viewport, vertex: *Vertex) void {
const x, const y, const z, const w = vertex.position;
const x_ndc = x / w;
const y_ndc = y / w;
const z_ndc = z / w;
const p_x = viewport.width;
const p_y = viewport.height;
const p_z = viewport.max_depth - viewport.min_depth;
const o_x = viewport.x + viewport.width / 2.0;
const o_y = viewport.y + viewport.height / 2.0;
const o_z = viewport.min_depth;
const x_screen = ((p_x / 2.0) * x_ndc) + o_x;
const y_screen = ((p_y / 2.0) * y_ndc) + o_y;
const z_screen = (p_z * z_ndc) + o_z;
vertex.position = zm.f32x4(x_screen, y_screen, z_screen, w);
}
fn clipDistance(position: F32x4, plane: ClipPlane) f32 {
const x, const y, const z, const w = position;
return switch (plane) {
.Left => x + w,
.Right => w - x,
.Bottom => y + w,
.Top => w - y,
.Near => z,
.Far => w - z,
};
}
fn isVertexInsidePlane(vertex: *const Vertex, plane: ClipPlane) bool {
return clipDistance(vertex.position, plane) >= 0.0;
}
fn interpolateBlob(allocator: std.mem.Allocator, a: []const u8, b: []const u8, t: f32) VkError![]u8 {
const len = @min(a.len, b.len);
const result = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
var byte_index: usize = 0;
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
const value_a = std.mem.bytesToValue(F32x4, a[byte_index..]);
const value_b = std.mem.bytesToValue(F32x4, b[byte_index..]);
base.utils.writePacked(F32x4, result[byte_index..], value_a + ((value_b - value_a) * zm.f32x4s(t)));
}
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) {
const value_a = std.mem.bytesToValue(f32, a[byte_index..]);
const value_b = std.mem.bytesToValue(f32, b[byte_index..]);
base.utils.writePacked(f32, result[byte_index..], value_a + ((value_b - value_a) * t));
}
if (byte_index < len)
@memcpy(result[byte_index..], a[byte_index..len]);
return result;
}
fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex, b: *const Vertex, t: f32) VkError!Vertex {
var result: Vertex = .{
.position = a.position + ((b.position - a.position) * zm.f32x4s(t)),
.outputs = undefined,
};
@memset(result.outputs[0..], null);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const out_a = a.outputs[location] orelse continue;
const out_b = b.outputs[location] orelse continue;
result.outputs[location] = .{
.interpolation_type = out_a.interpolation_type,
.blob = if (out_a.interpolation_type == .flat)
allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory
else
try interpolateBlob(allocator, out_a.blob, out_b.blob, t),
};
}
return result;
}
fn clipPolygonAgainstPlane(allocator: std.mem.Allocator, input: *const ClippedPolygon, plane: ClipPlane) VkError!ClippedPolygon {
var output: ClippedPolygon = .{};
if (input.len == 0)
return output;
var previous = input.vertices[input.len - 1];
var previous_inside = isVertexInsidePlane(&previous, plane);
var previous_distance = clipDistance(previous.position, plane);
for (input.vertices[0..input.len]) |current| {
const current_inside = isVertexInsidePlane(&current, plane);
const current_distance = clipDistance(current.position, plane);
if (current_inside != previous_inside) {
const t = previous_distance / (previous_distance - current_distance);
try output.append(try interpolateVertexForClipping(allocator, &previous, &current, t));
}
if (current_inside)
try output.append(current);
previous = current;
previous_inside = current_inside;
previous_distance = current_distance;
}
return output;
}
+71
View File
@@ -0,0 +1,71 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const zm = base.zm;
const spv = @import("spv");
const VertexInterpolation = @import("rasterizer/common.zig").VertexInterpolation;
const Renderer = @import("Renderer.zig");
const SoftImage = @import("../SoftImage.zig");
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
pub fn shaderInvocation(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
batch_id: usize,
position: zm.F32x4,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation,
) SpvRuntimeError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 {
const io = draw_call.renderer.device.interface.io();
_ = position;
const pipeline = draw_call.renderer.state.pipeline orelse return undefined;
const shader = pipeline.stages.getPtr(.fragment) orelse return undefined;
const runtime = &shader.runtimes[batch_id];
const mutex = &runtime.mutex;
const rt = &runtime.rt;
mutex.lock(io) catch return SpvRuntimeError.Unknown;
defer mutex.unlock(io);
try rt.populatePushConstants(draw_call.renderer.state.push_constant_blob[0..]);
const entry = try rt.getEntryPointByName(shader.entry);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const result_word = rt.getResultByLocation(@intCast(location), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
try rt.writeInput(inputs[location].blob, result_word);
if (inputs[location].free_responsability)
allocator.free(inputs[location].blob);
}
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds,
SpvRuntimeError.Killed,
=> {},
else => return err,
};
var outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8 = undefined;
@memset(std.mem.asBytes(&outputs), 0);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const result_word = rt.getResultByLocation(@intCast(location), .output) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
try rt.readOutput(&outputs[location], result_word);
}
try rt.flushDescriptorSets(allocator);
return outputs;
}
-65
View File
@@ -1,65 +0,0 @@
const std = @import("std");
const spv = @import("spv");
const base = @import("base");
const zm = base.zm;
const F32x4 = Renderer.F32x4;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const Renderer = @import("Renderer.zig");
const SoftPipeline = @import("../SoftPipeline.zig");
const VkError = base.VkError;
pub const RunData = struct {
renderer: *Renderer,
pipeline: *SoftPipeline,
batch_id: usize,
batch_size: usize,
fragment_count: usize,
draw_call: *Renderer.DrawCall,
};
pub fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
};
}
inline fn run(data: RunData) !void {
const allocator = data.renderer.device.device_allocator.allocator();
const shader = data.pipeline.stages.getPtrAssertContains(.fragment);
const rt = &shader.runtimes[data.batch_id];
const entry = try rt.getEntryPointByName(shader.entry);
const output_result = try rt.getResultByLocation(0, .output);
var invocation_index: usize = data.batch_id;
while (invocation_index < data.fragment_count) : (invocation_index += data.batch_size) {
const fragment: *Renderer.Fragment = &data.draw_call.fragments[invocation_index];
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const result_word = rt.getResultByLocation(@intCast(location), .input) catch |err| switch (err) {
SpvRuntimeError.NotFound => continue,
else => return err,
};
try rt.writeInput(fragment.inputs[location], result_word);
}
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds,
SpvRuntimeError.Killed,
=> {},
else => return err,
};
try rt.readOutput(std.mem.asBytes(&fragment.color), output_result);
fragment.color = std.math.clamp(fragment.color, zm.f32x4s(0.0), zm.f32x4s(1.0));
}
}
+267 -139
View File
@@ -1,172 +1,300 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const base = @import("base"); const base = @import("base");
const zm = base.zm;
const clip = @import("clip.zig");
const bresenham = @import("rasterizer/bresenham.zig");
const edge_function = @import("rasterizer/edge_function.zig");
const common = @import("rasterizer/common.zig");
const Renderer = @import("Renderer.zig");
const Vertex = Renderer.Vertex;
const DrawCall = Renderer.DrawCall;
const SoftImage = @import("../SoftImage.zig");
const VkError = base.VkError; const VkError = base.VkError;
const lib = @import("../lib.zig"); pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocator, draw_call: *DrawCall) VkError!void {
const io = draw_call.renderer.device.interface.io();
const Renderer = @import("Renderer.zig"); const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
const spv = @import("spv"); const topology = pipeline_data.input_assembly.topology;
pub const F32x4 = zm.F32x4; const color_attachments = draw_call.render_pass.interface.subpasses[renderer.subpass_index].color_attachments orelse return VkError.InvalidAttachmentDrv;
const color_attachment_access = allocator.alloc(?common.RenderTargetAccess, color_attachments.len) catch return VkError.OutOfDeviceMemory;
@memset(color_attachment_access, null);
fn writePacked(comptime T: type, bytes: []u8, value: T) void { for (color_attachments, color_attachment_access) |attachment_ref, *access| {
const raw: [@sizeOf(T)]u8 = @bitCast(value); if (attachment_ref.attachment == vk.ATTACHMENT_UNUSED)
@memcpy(bytes[0..@sizeOf(T)], raw[0..]);
}
fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32, b1: f32, b2: f32) F32x4 {
return (value0 * @as(F32x4, @splat(b0))) + (value1 * @as(F32x4, @splat(b1))) + (value2 * @as(F32x4, @splat(b2)));
}
fn interpolateVertexOutputs(
allocator: std.mem.Allocator,
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
v2: *const Renderer.Vertex,
b0: f32,
b1: f32,
b2: f32,
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][]u8 {
var inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][]u8 = undefined;
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const out0 = v0.outputs[location] orelse continue;
const out1 = v1.outputs[location] orelse continue;
const out2 = v2.outputs[location] orelse continue;
if (out0.interpolation_type == .flat or out0.blob.len == 0) {
inputs[location] = out0.blob;
continue; continue;
}
const len = @min(out0.blob.len, out1.blob.len, out2.blob.len); const render_target_view: *base.ImageView = draw_call.color_attachments[attachment_ref.attachment];
const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory; const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image));
var byte_index: usize = 0; const color_range = render_target_view.subresource_range;
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) { const color_format = render_target_view.format;
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]);
const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]);
writePacked(F32x4, input[byte_index..], interpolateF32x4(value0, value1, value2, b0, b1, b2));
}
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) { const color_attachment_subresource_offset = try render_target.getSubresourceOffset(
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]); color_range.aspect_mask,
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]); color_range.base_mip_level,
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]); color_range.base_array_layer,
writePacked(f32, input[byte_index..], (value0 * b0) + (value1 * b1) + (value2 * b2)); );
} const color_attachment_subresource_size = render_target.getLayerSize(color_range.aspect_mask);
access.* = .{
if (byte_index < len) .mutex = undefined,
@memcpy(input[byte_index..], out0.blob[byte_index..len]); .base = try render_target.mapAsSliceWithAddedOffset(u8, color_attachment_subresource_offset, color_attachment_subresource_size),
.row_pitch = render_target.getRowPitchMemSizeForMipLevelWithFormat(color_range.aspect_mask, color_range.base_mip_level, color_format),
inputs[location] = input; .texel_size = base.format.texelSize(color_format),
.format = color_format,
};
} }
return inputs; const depth_attachment_view: ?*base.ImageView = if (draw_call.depth_attachment) |view| view else null;
} const depth_attachment: ?*SoftImage = if (depth_attachment_view) |view| @alignCast(@fieldParentPtr("interface", view.image)) else null;
fn interpolateLineOutputs(allocator: std.mem.Allocator, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, t: f32) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][]u8 { var depth_attachment_access: ?common.RenderTargetAccess = blk: {
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0); if (depth_attachment == null)
} break :blk null;
pub fn drawLineBresenham(allocator: std.mem.Allocator, fragments: *std.ArrayList(Renderer.Fragment), v0: *Renderer.Vertex, v1: *Renderer.Vertex) VkError!void { const depth_range = depth_attachment_view.?.subresource_range;
var x0: i32 = @intFromFloat(v0.position[0]); const depth_format = depth_attachment_view.?.format;
var y0: i32 = @intFromFloat(v0.position[1]);
var x1: i32 = @intFromFloat(v1.position[0]);
var y1: i32 = @intFromFloat(v1.position[1]);
const steep = blk: { const attachment_subresource_offset = try depth_attachment.?.getSubresourceOffset(
if (@abs(y1 - y0) > @abs(x1 - x0)) { depth_range.aspect_mask,
std.mem.swap(i32, &x0, &y0); depth_range.base_mip_level,
std.mem.swap(i32, &x1, &y1); depth_range.base_array_layer,
break :blk true; );
} const attachment_subresource_size = depth_attachment.?.getLayerSize(depth_range.aspect_mask);
break :blk false; break :blk .{
.mutex = .init,
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_range.aspect_mask, depth_range.base_mip_level, depth_format),
.texel_size = base.format.texelSize(depth_format),
.format = depth_format,
};
}; };
var start_vertex = v0; switch (topology) {
var end_vertex = v1; .triangle_list => for (0..@divTrunc(draw_call.vertices.len, 3)) |triangle_index| {
if (x0 > x1) { const first_vertex = triangle_index * 3;
std.mem.swap(i32, &x0, &x1); const v0 = &draw_call.vertices[first_vertex + 0];
std.mem.swap(i32, &y0, &y1); const v1 = &draw_call.vertices[first_vertex + 1];
std.mem.swap(*Renderer.Vertex, &start_vertex, &end_vertex); const v2 = &draw_call.vertices[first_vertex + 2];
try clipTransformAndRasterizeTriangle(
renderer,
allocator,
draw_call,
v0,
v1,
v2,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
},
.triangle_fan => if (draw_call.vertices.len >= 3) {
const v0 = &draw_call.vertices[0];
for (1..(draw_call.vertices.len - 1)) |vertex_index| {
const v1 = &draw_call.vertices[vertex_index];
const v2 = &draw_call.vertices[vertex_index + 1];
try clipTransformAndRasterizeTriangle(
renderer,
allocator,
draw_call,
v0,
v1,
v2,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
}
},
.triangle_strip => if (draw_call.vertices.len >= 3) {
for (0..(draw_call.vertices.len - 2)) |vertex_index| {
const v0 = &draw_call.vertices[vertex_index + 0];
const v1 = &draw_call.vertices[vertex_index + 1];
const v2 = &draw_call.vertices[vertex_index + 2];
if ((vertex_index & 1) == 0) {
try clipTransformAndRasterizeTriangle(
renderer,
allocator,
draw_call,
v0,
v1,
v2,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
} else {
try clipTransformAndRasterizeTriangle(
renderer,
allocator,
draw_call,
v1,
v0,
v2,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
}
}
},
.line_list => for (0..@divTrunc(draw_call.vertices.len, 2)) |line_index| {
const first_vertex = line_index * 2;
const v0 = &draw_call.vertices[first_vertex + 0];
const v1 = &draw_call.vertices[first_vertex + 1];
try clipTransformAndRasterizeLine(
allocator,
draw_call,
v0,
v1,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
},
.line_strip => if (draw_call.vertices.len >= 2) {
for (0..(draw_call.vertices.len - 1)) |vertex_index| {
const v0 = &draw_call.vertices[vertex_index + 0];
const v1 = &draw_call.vertices[vertex_index + 1];
try clipTransformAndRasterizeLine(
allocator,
draw_call,
v0,
v1,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
);
}
},
else => base.unsupported("primitive topology {any}", .{topology}),
} }
const d_err = @abs(y1 - y0); draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
const d_x = x1 - x0;
const y_step: i32 = if (y0 > y1) -1 else 1;
var err = @divTrunc(d_x, 2); // Pixel center.
var y = y0;
var x = x0;
while (x <= x1) : (x += 1) {
const x_fragment: f32 = @floatFromInt(if (steep) y else x);
const y_fragment: f32 = @floatFromInt(if (steep) x else y);
const t = @as(f32, @floatFromInt(x - x0)) / @as(f32, @floatFromInt(@max(d_x, 1)));
const z = ((1.0 - t) * start_vertex.position[2]) + (t * end_vertex.position[2]);
fragments.append(allocator, .{
.position = zm.f32x4(x_fragment, y_fragment, z, 1.0),
.color = zm.f32x4(1.0, 1.0, 1.0, 1.0),
.inputs = try interpolateLineOutputs(allocator, start_vertex, end_vertex, t),
}) catch return VkError.OutOfDeviceMemory;
err -= @intCast(d_err);
if (err < 0) {
y += y_step;
err += d_x;
}
}
} }
fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 { fn clipTransformAndRasterizeLine(
return ((p[0] - a[0]) * (b[1] - a[1])) - ((p[1] - a[1]) * (b[0] - a[0])); allocator: std.mem.Allocator,
draw_call: *DrawCall,
v0: *Vertex,
v1: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const clipped_line = (try clip.clipLine(allocator, v0, v1)) orelse return;
var tv0 = clipped_line.v0;
var tv1 = clipped_line.v1;
clip.viewportTransformVertex(draw_call.viewport, &tv0);
clip.viewportTransformVertex(draw_call.viewport, &tv1);
try bresenham.drawLine(
allocator,
draw_call,
&tv0,
&tv1,
color_attachment_access,
depth_attachment_access,
);
} }
pub fn drawTriangleFilled(allocator: std.mem.Allocator, fragments: *std.ArrayList(Renderer.Fragment), v0: *Renderer.Vertex, v1: *Renderer.Vertex, v2: *Renderer.Vertex) VkError!void { fn clipTransformAndRasterizeTriangle(
const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0], v2.position[0]))); renderer: *Renderer,
const max_x: i32 = @intFromFloat(@ceil(@max(v0.position[0], v1.position[0], v2.position[0]))); allocator: std.mem.Allocator,
const min_y: i32 = @intFromFloat(@floor(@min(v0.position[1], v1.position[1], v2.position[1]))); draw_call: *DrawCall,
const max_y: i32 = @intFromFloat(@ceil(@max(v0.position[1], v1.position[1], v2.position[1]))); v0: *Vertex,
v1: *Vertex,
v2: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const clipped_polygon = try clip.clipTriangle(allocator, v0, v1, v2);
const area = edgeFunction(v0.position, v1.position, v2.position); if (clipped_polygon.len < 3)
if (area == 0.0)
return; return;
var y = min_y; for (1..(clipped_polygon.len - 1)) |vertex_index| {
while (y <= max_y) : (y += 1) { var tv0 = clipped_polygon.vertices[0];
var x = min_x; var tv1 = clipped_polygon.vertices[vertex_index];
while (x <= max_x) : (x += 1) { var tv2 = clipped_polygon.vertices[vertex_index + 1];
const p = zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, 0.0, 1.0);
const w0 = edgeFunction(v1.position, v2.position, p); clip.viewportTransformVertex(draw_call.viewport, &tv0);
const w1 = edgeFunction(v2.position, v0.position, p); clip.viewportTransformVertex(draw_call.viewport, &tv1);
const w2 = edgeFunction(v0.position, v1.position, p); clip.viewportTransformVertex(draw_call.viewport, &tv2);
const inside = if (area > 0.0) try rasterizeTriangle(
w0 >= 0.0 and w1 >= 0.0 and w2 >= 0.0 renderer,
else allocator,
w0 <= 0.0 and w1 <= 0.0 and w2 <= 0.0; draw_call,
&tv0,
if (!inside) &tv1,
continue; &tv2,
color_attachment_access,
const b0 = w0 / area; depth_attachment_access,
const b1 = w1 / area; );
const b2 = w2 / area;
const z = (b0 * v0.position[2]) + (b1 * v1.position[2]) + (b2 * v2.position[2]);
fragments.append(allocator, .{
.position = zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0),
.color = zm.f32x4(1.0, 1.0, 1.0, 1.0),
.inputs = try interpolateVertexOutputs(allocator, v0, v1, v2, b0, b1, b2),
}) catch return VkError.OutOfDeviceMemory;
}
} }
} }
fn rasterizeTriangle(
renderer: *Renderer,
allocator: std.mem.Allocator,
draw_call: *DrawCall,
v0: *Vertex,
v1: *Vertex,
v2: *Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
if (try triangleIsCulled(renderer, v0, v1, v2))
return;
draw_call.stats.polygons_drawn += 1;
const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
switch (pipeline_data.rasterization.polygon_mode) {
.fill => try edge_function.drawTriangle(allocator, draw_call, v0, v1, v2, color_attachment_access, depth_attachment_access),
.line => {
try bresenham.drawLine(allocator, draw_call, v0, v1, color_attachment_access, depth_attachment_access);
try bresenham.drawLine(allocator, draw_call, v1, v2, color_attachment_access, depth_attachment_access);
try bresenham.drawLine(allocator, draw_call, v2, v0, color_attachment_access, depth_attachment_access);
},
.point => {}, // TODO
else => base.unsupported("polygon mode {any}", .{pipeline_data.rasterization.polygon_mode}),
}
}
fn triangleIsCulled(renderer: *Renderer, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!bool {
const pipeline_data = (renderer.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
const rasterization = pipeline_data.rasterization;
const cull_mode = rasterization.cull_mode;
if (!cull_mode.front_bit and !cull_mode.back_bit)
return false;
if (cull_mode.front_bit and cull_mode.back_bit)
return true;
const area = triangleArea(v0, v1, v2);
if (area == 0.0)
return true;
const front_face = switch (rasterization.front_face) {
.counter_clockwise => area < 0.0,
.clockwise => area > 0.0,
else => return false,
};
return (cull_mode.front_bit and front_face) or (cull_mode.back_bit and !front_face);
}
inline fn triangleArea(v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) f32 {
const x0, const y0, _, _ = v0.position;
const x1, const y1, _, _ = v1.position;
const x2, const y2, _, _ = v2.position;
return ((x1 - x0) * (y2 - y0)) - ((y1 - y0) * (x2 - x0));
}
+181
View File
@@ -0,0 +1,181 @@
const std = @import("std");
const base = @import("base");
const spv = @import("spv");
const zm = base.zm;
const blitter = @import("../blitter.zig");
const common = @import("common.zig");
const fragment = @import("../fragment.zig");
const Renderer = @import("../Renderer.zig");
const SoftImage = @import("../../SoftImage.zig");
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const F32x4 = zm.F32x4;
const RunData = struct {
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
batch_id: usize,
x0: i32,
y0: i32,
d_x: i32,
d_err: i32,
y_step: i32,
steep: bool,
start_vertex: *Renderer.Vertex,
end_vertex: *Renderer.Vertex,
start_step: usize,
end_step: usize,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
};
pub fn drawLine(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
v0: *Renderer.Vertex,
v1: *Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
var x0: i32 = @intFromFloat(v0.position[0]);
var y0: i32 = @intFromFloat(v0.position[1]);
var x1: i32 = @intFromFloat(v1.position[0]);
var y1: i32 = @intFromFloat(v1.position[1]);
const steep = blk: {
if (@abs(y1 - y0) > @abs(x1 - x0)) {
std.mem.swap(i32, &x0, &y0);
std.mem.swap(i32, &x1, &y1);
break :blk true;
}
break :blk false;
};
var start_vertex = v0;
var end_vertex = v1;
if (x0 > x1) {
std.mem.swap(i32, &x0, &x1);
std.mem.swap(i32, &y0, &y1);
std.mem.swap(*Renderer.Vertex, &start_vertex, &end_vertex);
}
const d_err: i32 = @intCast(@abs(y1 - y0));
const d_x = x1 - x0;
const y_step: i32 = if (y0 > y1) -1 else 1;
const pipeline = draw_call.renderer.state.pipeline orelse return;
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
if (runtimes_count == 0)
return;
const step_count: usize = @as(usize, @intCast(d_x)) + 1;
const runs_count = @min(runtimes_count, step_count);
const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count);
var batch_id: usize = 0;
for (0..runs_count) |run_index| {
defer batch_id = @mod(batch_id + 1, runtimes_count);
const start_step = run_index * steps_per_run;
if (start_step >= step_count)
continue;
const end_step = @min(start_step + steps_per_run - 1, step_count - 1);
const run_data: RunData = .{
.allocator = allocator,
.draw_call = draw_call,
.batch_id = batch_id,
.x0 = x0,
.y0 = y0,
.d_x = d_x,
.d_err = d_err,
.y_step = y_step,
.steep = steep,
.start_vertex = start_vertex,
.end_vertex = end_vertex,
.start_step = start_step,
.end_step = end_step,
.color_attachment_access = color_attachment_access,
.depth_attachment_access = depth_attachment_access,
};
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
}
// Not syncing workers between triangles when rendering without depth buffer
// will lead to pixel rendering order issues between triangles.
if (depth_attachment_access == null)
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
}
fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32 {
if (d_x == 0)
return y0;
const numerator = (@as(i64, @intCast(step)) * @as(i64, d_err)) + @as(i64, @divTrunc(d_x - 1, 2));
const y_offset: i32 = @intCast(@divTrunc(numerator, @as(i64, d_x)));
return y0 + (y_step * y_offset);
}
fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("line fill mode catched a '{s}'", .{@errorName(err)});
if (comptime base.config.logs == .verbose) {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
}
};
}
inline fn run(data: RunData) !void {
var step = data.start_step;
while (step <= data.end_step) : (step += 1) {
const x = data.x0 + @as(i32, @intCast(step));
const y = bresenhamYAtStep(data.y0, data.d_x, data.d_err, data.y_step, step);
const pixel_x = if (data.steep) y else x;
const pixel_y = if (data.steep) x else y;
if (!common.scissorContainsPixel(data.draw_call.scissor, pixel_x, pixel_y)) {
continue;
}
const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1)));
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
// Early depth test to avoid unnecesary computations
if (data.depth_attachment_access) |depth| {
const offset = @as(usize, @intCast(pixel_x)) * depth.texel_size + @as(usize, @intCast(pixel_y)) * depth.row_pitch;
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
if (z >= depth_value[0])
continue;
}
const outputs = fragment.shaderInvocation(
data.allocator,
data.draw_call,
data.batch_id,
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0),
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
) catch |err| {
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
if (comptime base.config.logs == .verbose) {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
}
return;
};
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, @intCast(pixel_x), @intCast(pixel_y), z);
}
}
+143
View File
@@ -0,0 +1,143 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const zm = base.zm;
const spv = @import("spv");
const blitter = @import("../blitter.zig");
const Renderer = @import("../Renderer.zig");
const VkError = base.VkError;
const F32x4 = zm.F32x4;
const U32x4 = @Vector(4, u32);
pub const RenderTargetAccess = struct {
mutex: std.Io.Mutex,
base: []u8,
row_pitch: usize,
texel_size: usize,
format: vk.Format,
};
pub const VertexInterpolation = struct {
blob: []const u8,
free_responsability: bool,
};
pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
const min_x: i64 = @as(i64, scissor.offset.x);
const min_y: i64 = @as(i64, scissor.offset.y);
const max_x: i64 = min_x + @as(i64, @intCast(scissor.extent.width));
const max_y: i64 = min_y + @as(i64, @intCast(scissor.extent.height));
const pixel_x: i64 = @as(i64, x);
const pixel_y: i64 = @as(i64, y);
return pixel_x >= min_x and
pixel_x < max_x and
pixel_y >= min_y and
pixel_y < max_y;
}
pub fn interpolateVertexOutputs(
allocator: std.mem.Allocator,
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
v2: *const Renderer.Vertex,
b0: f32,
b1: f32,
b2: f32,
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
var inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation = undefined;
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
const out0 = v0.outputs[location] orelse continue;
const out1 = v1.outputs[location] orelse continue;
const out2 = v2.outputs[location] orelse continue;
if (out0.interpolation_type == .flat or out0.blob.len == 0) {
inputs[location] = .{ .blob = out0.blob, .free_responsability = false };
continue;
}
const len = @min(out0.blob.len, out1.blob.len, out2.blob.len);
const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
var byte_index: usize = 0;
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]);
const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]);
base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(value0, value1, value2, b0, b1, b2));
}
while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) {
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]);
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]);
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]);
base.utils.writePacked(f32, input[byte_index..], (value0 * b0) + (value1 * b1) + (value2 * b2));
}
if (byte_index < len)
@memcpy(input[byte_index..], out0.blob[byte_index..len]);
inputs[location] = .{ .blob = input, .free_responsability = true };
}
return inputs;
}
pub fn interpolateLineOutputs(
allocator: std.mem.Allocator,
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
t: f32,
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolation {
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0);
}
inline fn interpolateF32x4(value0: F32x4, value1: F32x4, value2: F32x4, b0: f32, b1: f32, b2: f32) F32x4 {
return (value0 * zm.f32x4s(b0)) + (value1 * zm.f32x4s(b1)) + (value2 * zm.f32x4s(b2));
}
inline fn fragmentOutputFloat4(output: [@sizeOf(F32x4)]u8, format: vk.Format) F32x4 {
const color = std.mem.bytesToValue(F32x4, &output);
return if (base.format.isSrgb(format)) zm.rgbToSrgb(color) else color;
}
pub fn writeToTargets(
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8,
draw_call: *Renderer.DrawCall,
color_attachment_access: []const ?RenderTargetAccess,
depth_attachment_access: ?*RenderTargetAccess,
x: usize,
y: usize,
z: f32,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
// After work depth test to avoid overwritten depth pixels during fragment invocations
if (depth_attachment_access) |depth| {
const depth_offset = @as(usize, @intCast(x)) * depth.texel_size + @as(usize, @intCast(y)) * depth.row_pitch;
depth.mutex.lock(io) catch return VkError.DeviceLost;
defer depth.mutex.unlock(io);
const depth_value = blitter.readFloat4(depth.base[depth_offset..], depth.format);
if (z >= depth_value[0])
return;
blitter.writeFloat4(zm.f32x4s(z), depth.base[depth_offset..], depth.format);
}
for (color_attachment_access, 0..) |maybe_color, location| {
const color = maybe_color orelse continue;
const color_offset = @as(usize, @intCast(x)) * color.texel_size + @as(usize, @intCast(y)) * color.row_pitch;
if (base.format.isUnnormalizedInteger(color.format)) {
blitter.writeInt4(std.mem.bytesToValue(U32x4, &outputs[location]), color.base[color_offset..], color.format);
} else {
blitter.writeFloat4(fragmentOutputFloat4(outputs[location], color.format), color.base[color_offset..], color.format);
}
}
}
@@ -0,0 +1,182 @@
const std = @import("std");
const vk = @import("vulkan");
const base = @import("base");
const spv = @import("spv");
const zm = base.zm;
const common = @import("common.zig");
const fragment = @import("../fragment.zig");
const blitter = @import("../blitter.zig");
const Renderer = @import("../Renderer.zig");
const VkError = base.VkError;
const SpvRuntimeError = spv.Runtime.RuntimeError;
const F32x4 = zm.F32x4;
const RunData = struct {
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
batch_id: usize,
min_x: i32,
max_x: i32,
min_y: i32,
max_y: i32,
area: f32,
v0: Renderer.Vertex,
v1: Renderer.Vertex,
v2: Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
};
pub fn drawTriangle(
allocator: std.mem.Allocator,
draw_call: *Renderer.DrawCall,
v0: *Renderer.Vertex,
v1: *Renderer.Vertex,
v2: *Renderer.Vertex,
color_attachment_access: []const ?common.RenderTargetAccess,
depth_attachment_access: ?*common.RenderTargetAccess,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0], v2.position[0])));
const max_x: i32 = @intFromFloat(@ceil(@max(v0.position[0], v1.position[0], v2.position[0])));
const min_y: i32 = @intFromFloat(@floor(@min(v0.position[1], v1.position[1], v2.position[1])));
const max_y: i32 = @intFromFloat(@ceil(@max(v0.position[1], v1.position[1], v2.position[1])));
const area = edgeFunction(v0.position, v1.position, v2.position);
if (area == 0.0)
return;
const pipeline = draw_call.renderer.state.pipeline orelse return;
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
const grid_size: usize = @intFromFloat(@ceil(@sqrt(@as(f32, @floatFromInt(runtimes_count)))));
const width: usize = @intCast(max_x - min_x + 1);
const height: usize = @intCast(max_y - min_y + 1);
const cols_per_run = @divTrunc(width + grid_size - 1, grid_size);
const rows_per_run = @divTrunc(height + grid_size - 1, grid_size);
var batch_id: usize = 0;
for (0..grid_size) |gy| {
for (0..grid_size) |gx| {
defer batch_id = @mod(batch_id + 1, runtimes_count);
const run_min_x = min_x + @as(i32, @intCast(gx * cols_per_run));
const run_min_y = min_y + @as(i32, @intCast(gy * rows_per_run));
if (run_min_x > max_x or run_min_y > max_y)
continue;
const run_max_x = @min(
run_min_x + @as(i32, @intCast(cols_per_run)) - 1,
max_x,
);
const run_max_y = @min(
run_min_y + @as(i32, @intCast(rows_per_run)) - 1,
max_y,
);
const run_data: RunData = .{
.allocator = allocator,
.draw_call = draw_call,
.batch_id = batch_id,
.v0 = v0.*,
.v1 = v1.*,
.v2 = v2.*,
.area = area,
.min_x = run_min_x,
.max_x = run_max_x,
.min_y = run_min_y,
.max_y = run_max_y,
.color_attachment_access = color_attachment_access,
.depth_attachment_access = depth_attachment_access,
};
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
}
}
// Not syncing workers between triangles when rendering without depth buffer
// will lead to pixel rendering order issues between triangles.
if (depth_attachment_access == null)
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
}
inline fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 {
return ((p[0] - a[0]) * (b[1] - a[1])) - ((p[1] - a[1]) * (b[0] - a[0]));
}
fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
if (comptime base.config.logs == .verbose) {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
}
};
}
inline fn run(data: RunData) !void {
var y = data.min_y;
while (y <= data.max_y) : (y += 1) {
var x = data.min_x;
while (x <= data.max_x) : (x += 1) {
if (!common.scissorContainsPixel(data.draw_call.scissor, x, y)) {
continue;
}
const p = zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, 0.0, 1.0);
const w0 = edgeFunction(data.v1.position, data.v2.position, p);
const w1 = edgeFunction(data.v2.position, data.v0.position, p);
const w2 = edgeFunction(data.v0.position, data.v1.position, p);
const inside = if (data.area > 0.0)
w0 >= 0.0 and w1 >= 0.0 and w2 >= 0.0
else
w0 <= 0.0 and w1 <= 0.0 and w2 <= 0.0;
if (!inside)
continue;
const b0 = w0 / data.area;
const b1 = w1 / data.area;
const b2 = w2 / data.area;
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
// Early depth test to avoid unnecesary computations
if (data.depth_attachment_access) |depth| {
const offset = @as(usize, @intCast(x)) * depth.texel_size + @as(usize, @intCast(y)) * depth.row_pitch;
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
if (z >= depth_value[0])
continue;
}
const outputs = fragment.shaderInvocation(
data.allocator,
data.draw_call,
data.batch_id,
zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0),
try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, b0, b1, b2),
) catch |err| {
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
if (comptime base.config.logs == .verbose) {
if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
}
return;
};
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, @intCast(x), @intCast(y), z);
}
}
}
+16 -11
View File
@@ -13,7 +13,6 @@ const VkError = base.VkError;
pub const RunData = struct { pub const RunData = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
renderer: *Renderer,
pipeline: *SoftPipeline, pipeline: *SoftPipeline,
batch_id: usize, batch_id: usize,
batch_size: usize, batch_size: usize,
@@ -28,17 +27,18 @@ pub const RunData = struct {
pub fn runWrapper(data: RunData) void { pub fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| { @call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"SPIR-V runtime").err("SPIR-V runtime catched a '{s}'", .{@errorName(err)});
if (@errorReturnTrace()) |trace| { if (comptime base.config.logs == .verbose) {
std.debug.dumpErrorReturnTrace(trace); if (@errorReturnTrace()) |trace| {
std.debug.dumpErrorReturnTrace(trace);
}
} }
}; };
} }
inline fn run(data: RunData) !void { inline fn run(data: RunData) !void {
const allocator = data.renderer.device.device_allocator.allocator();
const shader = data.pipeline.stages.getPtrAssertContains(.vertex); const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
const rt = &shader.runtimes[data.batch_id]; const rt = &shader.runtimes[data.batch_id].rt;
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
const entry = try rt.getEntryPointByName(shader.entry); const entry = try rt.getEntryPointByName(shader.entry);
@@ -58,19 +58,19 @@ inline fn run(data: RunData) !void {
const binding_info = (data.pipeline.interface.mode.graphics.input_assembly.binding_description orelse return)[attribute.binding]; const binding_info = (data.pipeline.interface.mode.graphics.input_assembly.binding_description orelse return)[attribute.binding];
const vertex_buffer = data.renderer.state.data.graphics.vertex_buffers[attribute.binding]; const vertex_buffer = data.draw_call.renderer.state.data.graphics.vertex_buffers[attribute.binding];
const buffer = vertex_buffer.buffer; const buffer = vertex_buffer.buffer;
const buffer_memory_size = base.format.texelSize(attribute.format); const buffer_memory_size = base.format.texelSize(attribute.format);
const buffer_memory = if (buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv; const buffer_memory = if (buffer.interface.memory) |memory| memory else return VkError.InvalidDeviceMemoryDrv;
const offset = buffer.interface.offset + vertex_buffer.offset + (binding_info.stride * vertex_index) + attribute.offset; const offset = buffer.interface.offset + vertex_buffer.offset + (binding_info.stride * vertex_index) + attribute.offset;
const buffer_memory_map: []u8 = @as([*]u8, @ptrCast(@alignCast(try buffer_memory.map(offset, buffer_memory_size))))[0..buffer_memory_size]; const buffer_memory_map: []u8 = try buffer_memory.map(offset, buffer_memory_size);
try rt.writeInput(buffer_memory_map, location_result); try rt.writeInput(buffer_memory_map, location_result);
} }
} }
rt.callEntryPoint(allocator, entry) catch |err| switch (err) { rt.callEntryPoint(data.allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored // Some errors can be safely ignored
SpvRuntimeError.OutOfBounds, SpvRuntimeError.OutOfBounds,
SpvRuntimeError.Killed, SpvRuntimeError.Killed,
@@ -92,10 +92,15 @@ inline fn run(data: RunData) !void {
}; };
try rt.readOutput(output.outputs[location].?.blob, result_word); try rt.readOutput(output.outputs[location].?.blob, result_word);
} }
try rt.flushDescriptorSets(data.allocator);
} }
} }
fn setupBuiltins(rt: *spv.Runtime, vertex_index: usize, instance_index: usize) !void { fn setupBuiltins(rt: *spv.Runtime, vertex_index: usize, instance_index: usize) !void {
try rt.writeBuiltIn(std.mem.asBytes(&vertex_index), .VertexIndex); const vertex_index_u32: u32 = @intCast(vertex_index);
try rt.writeBuiltIn(std.mem.asBytes(&instance_index), .InstanceIndex); const instance_index_u32: u32 = @intCast(instance_index);
try rt.writeBuiltIn(std.mem.asBytes(&vertex_index_u32), .VertexIndex);
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
} }
+4 -3
View File
@@ -60,15 +60,16 @@ pub const MIN_STORAGE_BUFFER_ALIGNMENT = 256;
pub const MAX_VERTEX_INPUT_BINDINGS = 16; pub const MAX_VERTEX_INPUT_BINDINGS = 16;
pub const MAX_VERTEX_INPUT_ATTRIBUTES = 32; pub const MAX_VERTEX_INPUT_ATTRIBUTES = 32;
pub const PUSH_CONSTANT_SIZE = 256;
pub const MAX_IMAGE_LEVELS_1D = 15; pub const MAX_IMAGE_LEVELS_1D = 15;
pub const MAX_IMAGE_LEVELS_2D = 15; pub const MAX_IMAGE_LEVELS_2D = 15;
pub const MAX_IMAGE_LEVELS_3D = 12; pub const MAX_IMAGE_LEVELS_3D = 12;
pub const MAX_IMAGE_LEVELS_CUBE = 15; pub const MAX_IMAGE_LEVELS_CUBE = 15;
pub const MAX_IMAGE_ARRAY_LAYERS = 2048; pub const MAX_IMAGE_ARRAY_LAYERS = 2048;
pub const PHYSICAL_DEVICE_HEAP_SIZE = 0x80000000; // 2 GiB pub const PHYSICAL_DEVICE_DEFAULT_NAME = "Ape software device";
pub const MAX_MEMORY_ALLOCATION_SIZE = 0x80000000; // 2 GiB pub const PHYSICAL_DEVICE_FALLBACK_HEAP_SIZE = 0x10000000; // 256MB
pub const MAX_ALLOCATION_COUNT = 4096;
pub const std_options = base.std_options; pub const std_options = base.std_options;
+10 -3
View File
@@ -1,16 +1,20 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const NonDispatchable = @import("NonDispatchable.zig");
const VkError = @import("error_set.zig").VkError; const VkError = @import("error_set.zig").VkError;
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const Device = @import("Device.zig"); const Device = @import("Device.zig");
const Buffer = @import("Buffer.zig");
const Self = @This(); const Self = @This();
pub const ObjectType: vk.ObjectType = .buffer_view; pub const ObjectType: vk.ObjectType = .buffer_view;
owner: *Device, owner: *Device,
buffer: *Buffer,
format: vk.Format,
offset: vk.DeviceSize,
range: vk.DeviceSize,
vtable: *const VTable, vtable: *const VTable,
@@ -20,10 +24,13 @@ pub const VTable = struct {
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.BufferViewCreateInfo) VkError!Self { pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.BufferViewCreateInfo) VkError!Self {
_ = allocator; _ = allocator;
_ = info;
return .{ return .{
.owner = device, .owner = device,
.buffer = try NonDispatchable(Buffer).fromHandleObject(info.buffer),
.vtable = undefined, .vtable = undefined,
.format = info.format,
.offset = info.offset,
.range = info.range,
}; };
} }
+29
View File
@@ -47,6 +47,7 @@ pub const DispatchTable = struct {
blitImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageBlit, vk.Filter) VkError!void, blitImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageBlit, vk.Filter) VkError!void,
clearAttachment: *const fn (*Self, vk.ClearAttachment, vk.ClearRect) VkError!void, clearAttachment: *const fn (*Self, vk.ClearAttachment, vk.ClearRect) VkError!void,
clearColorImage: *const fn (*Self, *Image, vk.ImageLayout, *const vk.ClearColorValue, vk.ImageSubresourceRange) VkError!void, clearColorImage: *const fn (*Self, *Image, vk.ImageLayout, *const vk.ClearColorValue, vk.ImageSubresourceRange) VkError!void,
clearDepthStencilImage: *const fn (*Self, *Image, vk.ImageLayout, *const vk.ClearDepthStencilValue, vk.ImageSubresourceRange) VkError!void,
copyBuffer: *const fn (*Self, *Buffer, *Buffer, []const vk.BufferCopy) VkError!void, copyBuffer: *const fn (*Self, *Buffer, *Buffer, []const vk.BufferCopy) VkError!void,
copyBufferToImage: *const fn (*Self, *Buffer, *Image, vk.ImageLayout, []const vk.BufferImageCopy) VkError!void, copyBufferToImage: *const fn (*Self, *Buffer, *Image, vk.ImageLayout, []const vk.BufferImageCopy) VkError!void,
copyImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageCopy) VkError!void, copyImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageCopy) VkError!void,
@@ -61,10 +62,14 @@ pub const DispatchTable = struct {
endRenderPass: *const fn (*Self) VkError!void, endRenderPass: *const fn (*Self) VkError!void,
executeCommands: *const fn (*Self, *Self) VkError!void, executeCommands: *const fn (*Self, *Self) VkError!void,
fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void, fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void,
nextSubpass: *const fn (*Self, vk.SubpassContents) VkError!void,
pipelineBarrier: *const fn (*Self, vk.PipelineStageFlags, vk.PipelineStageFlags, vk.DependencyFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void, pipelineBarrier: *const fn (*Self, vk.PipelineStageFlags, vk.PipelineStageFlags, vk.DependencyFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
pushConstants: *const fn (*Self, vk.ShaderStageFlags, u32, []const u8) VkError!void,
reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void, reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void,
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void,
setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void, setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void,
setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void, setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void,
waitEvent: *const fn (*Self, *Event, vk.PipelineStageFlags, vk.PipelineStageFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void, waitEvent: *const fn (*Self, *Event, vk.PipelineStageFlags, vk.PipelineStageFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
}; };
@@ -189,6 +194,12 @@ pub fn clearColorImage(self: *Self, image: *Image, layout: vk.ImageLayout, color
} }
} }
pub fn clearDepthStencilImage(self: *Self, image: *Image, layout: vk.ImageLayout, value: *const vk.ClearDepthStencilValue, ranges: []const vk.ImageSubresourceRange) VkError!void {
for (ranges) |range| {
try self.dispatch_table.clearDepthStencilImage(self, image, layout, value, range);
}
}
pub inline fn copyBuffer(self: *Self, src: *Buffer, dst: *Buffer, regions: []const vk.BufferCopy) VkError!void { pub inline fn copyBuffer(self: *Self, src: *Buffer, dst: *Buffer, regions: []const vk.BufferCopy) VkError!void {
try self.dispatch_table.copyBuffer(self, src, dst, regions); try self.dispatch_table.copyBuffer(self, src, dst, regions);
} }
@@ -241,6 +252,10 @@ pub inline fn fillBuffer(self: *Self, buffer: *Buffer, offset: vk.DeviceSize, si
try self.dispatch_table.fillBuffer(self, buffer, offset, size, data); try self.dispatch_table.fillBuffer(self, buffer, offset, size, data);
} }
pub inline fn nextSubpass(self: *Self, contents: vk.SubpassContents) VkError!void {
try self.dispatch_table.nextSubpass(self, contents);
}
pub inline fn pipelineBarrier( pub inline fn pipelineBarrier(
self: *Self, self: *Self,
src_stage: vk.PipelineStageFlags, src_stage: vk.PipelineStageFlags,
@@ -253,14 +268,28 @@ pub inline fn pipelineBarrier(
try self.dispatch_table.pipelineBarrier(self, src_stage, dst_stage, dependency, memory_barriers, buffer_barriers, image_barriers); try self.dispatch_table.pipelineBarrier(self, src_stage, dst_stage, dependency, memory_barriers, buffer_barriers, image_barriers);
} }
pub inline fn pushConstants(self: *Self, stages: vk.ShaderStageFlags, offset: u32, blob: []const u8) VkError!void {
try self.dispatch_table.pushConstants(self, stages, offset, blob);
}
pub inline fn resetEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void { pub inline fn resetEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void {
try self.dispatch_table.resetEvent(self, event, stage); try self.dispatch_table.resetEvent(self, event, stage);
} }
pub inline fn resolveImage(self: *Self, src: *Image, src_layout: vk.ImageLayout, dst: *Image, dst_layout: vk.ImageLayout, regions: []const vk.ImageResolve) VkError!void {
for (regions[0..]) |region| {
try self.dispatch_table.resolveImage(self, src, src_layout, dst, dst_layout, region);
}
}
pub inline fn setEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void { pub inline fn setEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void {
try self.dispatch_table.setEvent(self, event, stage); try self.dispatch_table.setEvent(self, event, stage);
} }
pub inline fn setScissor(self: *Self, first: u32, scissor: []const vk.Rect2D) VkError!void {
try self.dispatch_table.setScissor(self, first, scissor);
}
pub inline fn setViewport(self: *Self, first: u32, viewports: []const vk.Viewport) VkError!void { pub inline fn setViewport(self: *Self, first: u32, viewports: []const vk.Viewport) VkError!void {
try self.dispatch_table.setViewport(self, first, viewports); try self.dispatch_table.setViewport(self, first, viewports);
} }
+3 -3
View File
@@ -18,7 +18,7 @@ layout: *DescriptorSetLayout,
vtable: *const VTable, vtable: *const VTable,
pub const VTable = struct { pub const VTable = struct {
copy: *const fn (*Self, vk.CopyDescriptorSet) VkError!void, copy: *const fn (*Self, *const Self, vk.CopyDescriptorSet) VkError!void,
destroy: *const fn (*Self, std.mem.Allocator) void, destroy: *const fn (*Self, std.mem.Allocator) void,
write: *const fn (*Self, vk.WriteDescriptorSet) VkError!void, write: *const fn (*Self, vk.WriteDescriptorSet) VkError!void,
}; };
@@ -33,8 +33,8 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, layout: *DescriptorSe
}; };
} }
pub inline fn copy(self: *Self, copy_data: vk.CopyDescriptorSet) VkError!void { pub inline fn copy(self: *Self, src: *const Self, copy_data: vk.CopyDescriptorSet) VkError!void {
try self.vtable.copy(self, copy_data); try self.vtable.copy(self, src, copy_data);
} }
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void { pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
-13
View File
@@ -34,19 +34,6 @@ dynamic_descriptor_count: usize,
/// Shader stages affected by this descriptor set /// Shader stages affected by this descriptor set
stages: vk.ShaderStageFlags, stages: vk.ShaderStageFlags,
/// Mesa's common Vulkan runtime states:
///
/// It's often necessary to store a pointer to the descriptor set layout in
/// the descriptor so that any entrypoint which has access to a descriptor
/// set also has the layout. While layouts are often passed into various
/// entrypoints, they're notably missing from vkUpdateDescriptorSets(). In
/// order to implement descriptor writes, you either need to stash a pointer
/// to the descriptor set layout in the descriptor set or you need to copy
/// all of the relevant information. Storing a pointer is a lot cheaper.
///
/// Because descriptor set layout lifetimes and descriptor set lifetimes are
/// not guaranteed to coincide, we have to reference count if we're going to
/// do this.
ref_count: std.atomic.Value(usize), ref_count: std.atomic.Value(usize),
vtable: *const VTable, vtable: *const VTable,
+2 -2
View File
@@ -18,7 +18,7 @@ pub const VTable = struct {
destroy: *const fn (*Self, std.mem.Allocator) void, destroy: *const fn (*Self, std.mem.Allocator) void,
flushRange: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError!void, flushRange: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError!void,
invalidateRange: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError!void, invalidateRange: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError!void,
map: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError!?*anyopaque, map: *const fn (*Self, vk.DeviceSize, vk.DeviceSize) VkError![]u8,
unmap: *const fn (*Self) void, unmap: *const fn (*Self) void,
}; };
@@ -44,7 +44,7 @@ pub inline fn invalidateRange(self: *Self, offset: vk.DeviceSize, size: vk.Devic
try self.vtable.invalidateRange(self, offset, size); try self.vtable.invalidateRange(self, offset, size);
} }
pub inline fn map(self: *Self, offset: vk.DeviceSize, size: vk.DeviceSize) VkError!?*anyopaque { pub inline fn map(self: *Self, offset: vk.DeviceSize, size: vk.DeviceSize) VkError![]u8 {
return self.vtable.map(self, offset, size); return self.vtable.map(self, offset, size);
} }
-2
View File
@@ -6,8 +6,6 @@ const VkError = @import("error_set.zig").VkError;
const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable; const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const Device = @import("Device.zig"); const Device = @import("Device.zig");
const DeviceMemory = @import("DeviceMemory.zig");
const Image = @import("Image.zig"); const Image = @import("Image.zig");
const Self = @This(); const Self = @This();
+14 -2
View File
@@ -1,12 +1,13 @@
const std = @import("std"); const std = @import("std");
const vk = @import("vulkan"); const vk = @import("vulkan");
const NonDispatchable = @import("NonDispatchable.zig"); const NonDispatchable = @import("NonDispatchable.zig").NonDispatchable;
const VkError = @import("error_set.zig").VkError; const VkError = @import("error_set.zig").VkError;
const Device = @import("Device.zig"); const Device = @import("Device.zig");
const PipelineCache = @import("PipelineCache.zig"); const PipelineCache = @import("PipelineCache.zig");
const PipelineLayout = @import("PipelineLayout.zig");
const Self = @This(); const Self = @This();
pub const ObjectType: vk.ObjectType = .pipeline; pub const ObjectType: vk.ObjectType = .pipeline;
@@ -28,6 +29,7 @@ owner: *Device,
vtable: *const VTable, vtable: *const VTable,
bind_point: vk.PipelineBindPoint, bind_point: vk.PipelineBindPoint,
stages: vk.ShaderStageFlags, stages: vk.ShaderStageFlags,
layout: *PipelineLayout,
mode: union(enum) { mode: union(enum) {
compute: struct {}, compute: struct {},
graphics: struct { graphics: struct {
@@ -55,14 +57,18 @@ pub const VTable = struct {
}; };
pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!Self { pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.ComputePipelineCreateInfo) VkError!Self {
_ = allocator;
_ = cache; _ = cache;
const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout);
layout.ref();
errdefer layout.unref(allocator);
return .{ return .{
.owner = device, .owner = device,
.vtable = undefined, .vtable = undefined,
.bind_point = .compute, .bind_point = .compute,
.stages = info.stage.stage, .stages = info.stage.stage,
.layout = layout,
.mode = .{ .compute = .{} }, .mode = .{ .compute = .{} },
}; };
} }
@@ -70,6 +76,10 @@ pub fn initCompute(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipel
pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!Self { pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*PipelineCache, info: *const vk.GraphicsPipelineCreateInfo) VkError!Self {
_ = cache; _ = cache;
const layout = try NonDispatchable(PipelineLayout).fromHandleObject(info.layout);
layout.ref();
errdefer layout.unref(allocator);
var stages: vk.ShaderStageFlags = .{}; var stages: vk.ShaderStageFlags = .{};
if (info.p_stages) |p_stages| { if (info.p_stages) |p_stages| {
for (p_stages[0..info.stage_count]) |stage| { for (p_stages[0..info.stage_count]) |stage| {
@@ -82,6 +92,7 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.vtable = undefined, .vtable = undefined,
.bind_point = .graphics, .bind_point = .graphics,
.stages = stages, .stages = stages,
.layout = layout,
.mode = .{ .mode = .{
.graphics = .{ .graphics = .{
.input_assembly = .{ .input_assembly = .{
@@ -172,5 +183,6 @@ pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
} }
}, },
} }
self.layout.unref(allocator);
self.vtable.destroy(self, allocator); self.vtable.destroy(self, allocator);
} }
-13
View File
@@ -23,19 +23,6 @@ dynamic_descriptor_offsets: [lib.VULKAN_MAX_DESCRIPTOR_SETS]usize,
push_ranges_count: usize, push_ranges_count: usize,
push_ranges: [lib.VULKAN_MAX_PUSH_CONSTANT_RANGES]vk.PushConstantRange, push_ranges: [lib.VULKAN_MAX_PUSH_CONSTANT_RANGES]vk.PushConstantRange,
/// Mesa's common Vulkan runtime states:
///
/// It's often necessary to store a pointer to the descriptor set layout in
/// the descriptor so that any entrypoint which has access to a descriptor
/// set also has the layout. While layouts are often passed into various
/// entrypoints, they're notably missing from vkUpdateDescriptorSets(). In
/// order to implement descriptor writes, you either need to stash a pointer
/// to the descriptor set layout in the descriptor set or you need to copy
/// all of the relevant information. Storing a pointer is a lot cheaper.
///
/// Because descriptor set layout lifetimes and descriptor set lifetimes are
/// not guaranteed to coincide, we have to reference count if we're going to
/// do this.
ref_count: std.atomic.Value(usize), ref_count: std.atomic.Value(usize),
vtable: *const VTable, vtable: *const VTable,
+49
View File
@@ -10,8 +10,19 @@ const Device = @import("Device.zig");
const Self = @This(); const Self = @This();
pub const ObjectType: vk.ObjectType = .render_pass; pub const ObjectType: vk.ObjectType = .render_pass;
const SubpassDescription = struct {
flags: vk.SubpassDescriptionFlags,
pipeline_bind_point: vk.PipelineBindPoint,
input_attachments: ?[]const vk.AttachmentReference,
color_attachments: ?[]const vk.AttachmentReference,
resolve_attachments: ?[]const vk.AttachmentReference,
depth_stencil_attachments: ?vk.AttachmentReference,
preserve_attachments: ?[]const u32,
};
owner: *Device, owner: *Device,
attachments: []vk.AttachmentDescription, attachments: []vk.AttachmentDescription,
subpasses: []SubpassDescription,
vtable: *const VTable, vtable: *const VTable,
@@ -33,14 +44,52 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Rende
return VkError.ValidationFailed; return VkError.ValidationFailed;
} }
const subpasses = allocator.alloc(SubpassDescription, info.subpass_count) catch return VkError.OutOfHostMemory;
errdefer allocator.free(subpasses);
for (subpasses[0..], info.p_subpasses[0..]) |*subpass, subpass_info| {
subpass.* = .{
.flags = subpass_info.flags,
.pipeline_bind_point = subpass_info.pipeline_bind_point,
.input_attachments = if (subpass_info.p_input_attachments) |subpass_attachments|
allocator.dupe(vk.AttachmentReference, subpass_attachments[0..subpass_info.input_attachment_count]) catch return VkError.OutOfHostMemory
else
null,
.color_attachments = if (subpass_info.p_color_attachments) |subpass_attachments|
allocator.dupe(vk.AttachmentReference, subpass_attachments[0..subpass_info.color_attachment_count]) catch return VkError.OutOfHostMemory
else
null,
.resolve_attachments = if (subpass_info.p_resolve_attachments) |subpass_attachments|
allocator.dupe(vk.AttachmentReference, subpass_attachments[0..subpass_info.color_attachment_count]) catch return VkError.OutOfHostMemory
else
null,
.depth_stencil_attachments = if (subpass_info.p_depth_stencil_attachment) |subpass_attachment|
if (subpass_attachment.attachment != vk.ATTACHMENT_UNUSED) subpass_attachment.* else null
else
null,
.preserve_attachments = if (subpass_info.p_preserve_attachments) |subpass_attachments|
allocator.dupe(u32, subpass_attachments[0..subpass_info.preserve_attachment_count]) catch return VkError.OutOfHostMemory
else
null,
};
}
return .{ return .{
.owner = device, .owner = device,
.attachments = attachments, .attachments = attachments,
.subpasses = subpasses,
.vtable = undefined, .vtable = undefined,
}; };
} }
pub fn destroy(self: *Self, allocator: std.mem.Allocator) void { pub fn destroy(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(self.attachments); allocator.free(self.attachments);
for (self.subpasses[0..]) |subpass| {
if (subpass.input_attachments) |attachments| allocator.free(attachments);
if (subpass.color_attachments) |attachments| allocator.free(attachments);
if (subpass.resolve_attachments) |attachments| allocator.free(attachments);
if (subpass.preserve_attachments) |attachments| allocator.free(attachments);
}
allocator.free(self.subpasses);
self.vtable.destroy(self, allocator); self.vtable.destroy(self, allocator);
} }
+10 -1
View File
@@ -11,6 +11,11 @@ const Self = @This();
pub const ObjectType: vk.ObjectType = .sampler; pub const ObjectType: vk.ObjectType = .sampler;
owner: *Device, owner: *Device,
mag_filter: vk.Filter,
min_filter: vk.Filter,
address_mode_u: vk.SamplerAddressMode,
address_mode_v: vk.SamplerAddressMode,
address_mode_w: vk.SamplerAddressMode,
vtable: *const VTable, vtable: *const VTable,
@@ -20,9 +25,13 @@ pub const VTable = struct {
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.SamplerCreateInfo) VkError!Self { pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.SamplerCreateInfo) VkError!Self {
_ = allocator; _ = allocator;
_ = info;
return .{ return .{
.owner = device, .owner = device,
.mag_filter = info.mag_filter,
.min_filter = info.min_filter,
.address_mode_u = info.address_mode_u,
.address_mode_v = info.address_mode_v,
.address_mode_w = info.address_mode_w,
.vtable = undefined, .vtable = undefined,
}; };
} }
+1
View File
@@ -55,6 +55,7 @@ pub const VkError = error{
InvalidHandleDrv, InvalidHandleDrv,
InvalidPipelineDrv, InvalidPipelineDrv,
InvalidDeviceMemoryDrv, InvalidDeviceMemoryDrv,
InvalidAttachmentDrv,
}; };
pub inline fn errorLogger(err: VkError) void { pub inline fn errorLogger(err: VkError) void {
+149
View File
@@ -423,3 +423,152 @@ pub fn isUnsignedComponent(format: vk.Format, component: usize) bool {
pub inline fn isUnsigned(format: vk.Format) bool { pub inline fn isUnsigned(format: vk.Format) bool {
return isUnsignedComponent(format, 0); return isUnsignedComponent(format, 0);
} }
pub inline fn isUnnormalizedInteger(format: vk.Format) bool {
return isSint(format) or isUint(format);
}
pub inline fn isSscaled(format: vk.Format) bool {
return lib.c.vkuFormatIsSSCALED(@intCast(@intFromEnum(format)));
}
pub inline fn isUscaled(format: vk.Format) bool {
return lib.c.vkuFormatIsUSCALED(@intCast(@intFromEnum(format)));
}
fn maxComponentBits(format: vk.Format) u32 {
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 64)) return 64;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 32)) return 32;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 24)) return 24;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 16)) return 16;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 11)) return 11;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 10)) return 10;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 8)) return 8;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 6)) return 6;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 5)) return 5;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 4)) return 4;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 2)) return 2;
if (lib.c.vkuFormatHasComponentSize(@intCast(@intFromEnum(format)), 1)) return 1;
lib.unsupported("format component bits {any}", .{format});
return 0;
}
fn maxUnsignedValue(bits: u32) f32 {
return switch (bits) {
1 => 1.0,
2 => 3.0,
4 => 15.0,
5 => 31.0,
6 => 63.0,
8 => @as(f32, @floatFromInt(std.math.maxInt(u8))),
10 => 1023.0,
11 => 2047.0,
16 => @as(f32, @floatFromInt(std.math.maxInt(u16))),
24 => 0xffffff,
32 => @as(f32, @floatFromInt(std.math.maxInt(u32))),
64 => @as(f32, @floatFromInt(std.math.maxInt(u64))),
else => blk: {
lib.unsupported("format component bits {d}", .{bits});
break :blk 1.0;
},
};
}
fn maxSignedValue(bits: u32) f32 {
return switch (bits) {
2 => 1.0,
4 => 7.0,
5 => 15.0,
6 => 31.0,
8 => @as(f32, @floatFromInt(std.math.maxInt(i8))),
10 => 511.0,
11 => 1023.0,
16 => @as(f32, @floatFromInt(std.math.maxInt(i16))),
24 => 0x7fffff,
32 => @as(f32, @floatFromInt(std.math.maxInt(i32))),
64 => @as(f32, @floatFromInt(std.math.maxInt(i64))),
else => blk: {
lib.unsupported("format component bits {d}", .{bits});
break :blk 1.0;
},
};
}
fn minSignedValue(bits: u32) f32 {
return switch (bits) {
2 => -2.0,
4 => -8.0,
5 => -16.0,
6 => -32.0,
8 => @as(f32, @floatFromInt(std.math.minInt(i8))),
10 => -512.0,
11 => -1024.0,
16 => @as(f32, @floatFromInt(std.math.minInt(i16))),
24 => -0x800000,
32 => @as(f32, @floatFromInt(std.math.minInt(i32))),
64 => @as(f32, @floatFromInt(std.math.minInt(i64))),
else => blk: {
lib.unsupported("format component bits {d}", .{bits});
break :blk -1.0;
},
};
}
fn maxFloatValue(format: vk.Format) f32 {
return switch (format) {
.r16_sfloat,
.r16g16_sfloat,
.r16g16b16_sfloat,
.r16g16b16a16_sfloat,
.b10g11r11_ufloat_pack32,
.e5b9g9r9_ufloat_pack32,
.bc6h_ufloat_block,
.bc6h_sfloat_block,
=> std.math.floatMax(f16),
else => std.math.floatMax(f32),
};
}
pub fn maxElementValue(format: vk.Format) f32 {
if (isDepth(format))
return 1.0;
if (isStencil(format))
return maxUnsignedValue(8);
if (isSnorm(format) or isUnorm(format) or isSrgb(format))
return 1.0;
if (isUscaled(format) or isUint(format))
return maxUnsignedValue(maxComponentBits(format));
if (isSscaled(format) or isSint(format))
return maxSignedValue(maxComponentBits(format));
if (isUfloat(format) or isSfloat(format))
return maxFloatValue(format);
lib.unsupported("format max element value {any}", .{format});
return 1.0;
}
pub fn minElementValue(format: vk.Format) f32 {
if (isDepth(format) or isStencil(format))
return 0.0;
if (isSnorm(format))
return -1.0;
if (isUnorm(format) or isSrgb(format) or isUscaled(format) or isUint(format) or isUfloat(format))
return 0.0;
if (isSscaled(format) or isSint(format))
return minSignedValue(maxComponentBits(format));
if (isSfloat(format))
return -maxFloatValue(format);
lib.unsupported("format min element value {any}", .{format});
return 0.0;
}
+2 -4
View File
@@ -12,6 +12,7 @@ pub const lib_vulkan = @import("lib_vulkan.zig");
pub const logger = @import("logger.zig"); pub const logger = @import("logger.zig");
pub const format = @import("format.zig"); pub const format = @import("format.zig");
pub const config = @import("config"); pub const config = @import("config");
pub const utils = @import("utils.zig");
pub const Dispatchable = @import("Dispatchable.zig").Dispatchable; pub const Dispatchable = @import("Dispatchable.zig").Dispatchable;
pub const fallback_host_allocator = @import("fallback_host_allocator.zig").fallback_host_allocator; pub const fallback_host_allocator = @import("fallback_host_allocator.zig").fallback_host_allocator;
@@ -53,11 +54,8 @@ pub const SwapchainKHR = @import("wsi/SwapchainKHR.zig");
pub const VULKAN_VENDOR_ID = @typeInfo(vk.VendorId).@"enum".fields[@typeInfo(vk.VendorId).@"enum".fields.len - 1].value + 1; pub const VULKAN_VENDOR_ID = @typeInfo(vk.VendorId).@"enum".fields[@typeInfo(vk.VendorId).@"enum".fields.len - 1].value + 1;
pub const DRIVER_DEBUG_ALLOCATOR_ENV_NAME = "STROLL_DEBUG_ALLOCATOR";
pub const DRIVER_LOGS_ENV_NAME = "STROLL_LOGS_LEVEL";
/// Default driver name /// Default driver name
pub const DRIVER_NAME = "Unnamed Stroll Driver"; pub const DRIVER_NAME = "Unnamed Ape Driver";
/// Default Vulkan version /// Default Vulkan version
pub const VULKAN_VERSION = vk.makeApiVersion(0, 1, 0, 0); pub const VULKAN_VERSION = vk.makeApiVersion(0, 1, 0, 0);
+175 -212
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -59,7 +59,7 @@ pub fn log(comptime level: std.log.Level, comptime scope: @EnumLiteral(), compti
file.lock(io, .exclusive) catch {}; file.lock(io, .exclusive) catch {};
defer file.unlock(io); defer file.unlock(io);
const now = std.Io.Timestamp.now(io, .cpu_process).toMicroseconds(); const now = std.Io.Timestamp.now(io, .real).toMicroseconds();
const now_us: u16 = @intCast(@mod(now, 1000)); const now_us: u16 = @intCast(@mod(now, 1000));
const now_ms: u16 = @intCast(@mod(@divTrunc(now, 1000), std.time.ms_per_s)); const now_ms: u16 = @intCast(@mod(@divTrunc(now, 1000), std.time.ms_per_s));
@@ -87,7 +87,7 @@ pub fn log(comptime level: std.log.Level, comptime scope: @EnumLiteral(), compti
}; };
term.setColor(.magenta) catch {}; term.setColor(.magenta) catch {};
writer.writeAll("[StrollDriver") catch continue; writer.writeAll("[ApeDriver") catch continue;
if (!builtin.is_test) { if (!builtin.is_test) {
term.setColor(.cyan) catch {}; term.setColor(.cyan) catch {};
writer.writeAll(" " ++ root.DRIVER_NAME ++ " ") catch continue; writer.writeAll(" " ++ root.DRIVER_NAME ++ " ") catch continue;
+4
View File
@@ -0,0 +1,4 @@
pub fn writePacked(comptime T: type, bytes: []u8, value: T) void {
const raw: [@sizeOf(T)]u8 = @bitCast(value);
@memcpy(bytes[0..@sizeOf(T)], raw[0..]);
}
+1 -1
View File
@@ -156,7 +156,7 @@ pub fn presentImage(interface: *Interface, allocator: std.mem.Allocator, image:
} }
fn createShmFile(size: usize) VkError!std.posix.fd_t { fn createShmFile(size: usize) VkError!std.posix.fd_t {
const name = "stroll_vk_wayland_surface"; const name = "ape_vk_wayland_surface";
const fd = std.posix.memfd_create(name, std.posix.FD_CLOEXEC) catch return VkError.Unknown; const fd = std.posix.memfd_create(name, std.posix.FD_CLOEXEC) catch return VkError.Unknown;
errdefer std.c.close(fd); errdefer std.c.close(fd);
+10 -13
View File
@@ -65,10 +65,7 @@ pub fn load() VkError!void {
if (ref_count.load(.monotonic) != 0) if (ref_count.load(.monotonic) != 0)
return; return;
module = std.DynLib.open("libwayland-client.so.0") catch { module = std.DynLib.open("libwayland-client.so.0") catch return VkError.Unknown;
_ = ref_count.fetchSub(1, .monotonic);
return VkError.Unknown;
};
errdefer module.close(); errdefer module.close();
errdefer std.log.scoped(.WaylandClient).err("Could not open 'libwayland-client.so.0': {s}", .{std.c.dlerror() orelse "unknown error"}); errdefer std.log.scoped(.WaylandClient).err("Could not open 'libwayland-client.so.0': {s}", .{std.c.dlerror() orelse "unknown error"});
@@ -94,7 +91,7 @@ pub fn unload() void {
} }
} }
pub fn wl_registry_bind(registry: *wl_registry, name: u32, interface: *const wl_interface, version: u32) callconv(.c) ?*wl_proxy { pub fn wl_registry_bind(registry: *wl_registry, name: u32, interface: *const wl_interface, version: u32) ?*wl_proxy {
return wl_proxy_marshal_flags( return wl_proxy_marshal_flags(
@ptrCast(@alignCast(registry)), @ptrCast(@alignCast(registry)),
WL_REGISTRY_BIND, WL_REGISTRY_BIND,
@@ -108,7 +105,7 @@ pub fn wl_registry_bind(registry: *wl_registry, name: u32, interface: *const wl_
); );
} }
pub fn wl_display_get_registry(display: *wl_display) callconv(.c) ?*wl_registry { pub fn wl_display_get_registry(display: *wl_display) ?*wl_registry {
return @ptrCast(@alignCast(wl_proxy_marshal_flags( return @ptrCast(@alignCast(wl_proxy_marshal_flags(
@ptrCast(@alignCast(display)), @ptrCast(@alignCast(display)),
WL_DISPLAY_GET_REGISTRY, WL_DISPLAY_GET_REGISTRY,
@@ -119,7 +116,7 @@ pub fn wl_display_get_registry(display: *wl_display) callconv(.c) ?*wl_registry
))); )));
} }
pub fn wl_registry_add_listener(registry: *wl_registry, listener: *const wl_registry_listener, data: ?*anyopaque) callconv(.c) c_int { pub fn wl_registry_add_listener(registry: *wl_registry, listener: *const wl_registry_listener, data: ?*anyopaque) c_int {
return wl_proxy_add_listener(@ptrCast(@alignCast(registry)), @ptrCast(@alignCast(@constCast(listener))), data); return wl_proxy_add_listener(@ptrCast(@alignCast(registry)), @ptrCast(@alignCast(@constCast(listener))), data);
} }
@@ -136,7 +133,7 @@ pub fn wl_shm_create_pool(shm: *wl_shm, fd: i32, size: i32) callconv(.c) ?*wl_sh
))); )));
} }
pub fn wl_shm_pool_destroy(shm_pool: *wl_shm_pool) callconv(.c) void { pub fn wl_shm_pool_destroy(shm_pool: *wl_shm_pool) void {
_ = wl_proxy_marshal_flags( _ = wl_proxy_marshal_flags(
@ptrCast(@alignCast(shm_pool)), @ptrCast(@alignCast(shm_pool)),
WL_SHM_POOL_DESTROY, WL_SHM_POOL_DESTROY,
@@ -146,7 +143,7 @@ pub fn wl_shm_pool_destroy(shm_pool: *wl_shm_pool) callconv(.c) void {
); );
} }
pub fn wl_shm_pool_create_buffer(shm_pool: *wl_shm_pool, offset: i32, width: i32, height: i32, stride: i32, format: u32) callconv(.c) ?*wl_buffer { pub fn wl_shm_pool_create_buffer(shm_pool: *wl_shm_pool, offset: i32, width: i32, height: i32, stride: i32, format: u32) ?*wl_buffer {
return @ptrCast(@alignCast(wl_proxy_marshal_flags( return @ptrCast(@alignCast(wl_proxy_marshal_flags(
@ptrCast(@alignCast(shm_pool)), @ptrCast(@alignCast(shm_pool)),
WL_SHM_POOL_CREATE_BUFFER, WL_SHM_POOL_CREATE_BUFFER,
@@ -162,7 +159,7 @@ pub fn wl_shm_pool_create_buffer(shm_pool: *wl_shm_pool, offset: i32, width: i32
))); )));
} }
pub fn wl_buffer_destroy(buffer: *wl_buffer) callconv(.c) void { pub fn wl_buffer_destroy(buffer: *wl_buffer) void {
_ = wl_proxy_marshal_flags( _ = wl_proxy_marshal_flags(
@ptrCast(@alignCast(buffer)), @ptrCast(@alignCast(buffer)),
WL_BUFFER_DESTROY, WL_BUFFER_DESTROY,
@@ -172,7 +169,7 @@ pub fn wl_buffer_destroy(buffer: *wl_buffer) callconv(.c) void {
); );
} }
pub fn wl_surface_attach(surface: *wl_surface, buffer: *wl_buffer, x: i32, y: i32) callconv(.c) void { pub fn wl_surface_attach(surface: *wl_surface, buffer: *wl_buffer, x: i32, y: i32) void {
_ = wl_proxy_marshal_flags( _ = wl_proxy_marshal_flags(
@ptrCast(@alignCast(surface)), @ptrCast(@alignCast(surface)),
WL_SURFACE_ATTACH, WL_SURFACE_ATTACH,
@@ -185,7 +182,7 @@ pub fn wl_surface_attach(surface: *wl_surface, buffer: *wl_buffer, x: i32, y: i3
); );
} }
pub fn wl_surface_damage(surface: *wl_surface, x: i32, y: i32, width: i32, height: i32) callconv(.c) void { pub fn wl_surface_damage(surface: *wl_surface, x: i32, y: i32, width: i32, height: i32) void {
_ = wl_proxy_marshal_flags( _ = wl_proxy_marshal_flags(
@ptrCast(@alignCast(surface)), @ptrCast(@alignCast(surface)),
WL_SURFACE_DAMAGE, WL_SURFACE_DAMAGE,
@@ -199,7 +196,7 @@ pub fn wl_surface_damage(surface: *wl_surface, x: i32, y: i32, width: i32, heigh
); );
} }
pub fn wl_surface_commit(surface: *wl_surface) callconv(.c) void { pub fn wl_surface_commit(surface: *wl_surface) void {
_ = wl_proxy_marshal_flags( _ = wl_proxy_marshal_flags(
@ptrCast(@alignCast(surface)), @ptrCast(@alignCast(surface)),
WL_SURFACE_COMMIT, WL_SURFACE_COMMIT,