Compare commits
37 Commits
215486ac34
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
7da8eec788
|
|||
|
e68ee5217d
|
|||
|
191db8a8e4
|
|||
|
657bb38810
|
|||
|
8cd40c40f0
|
|||
|
042662a928
|
|||
|
d4e835b6af
|
|||
|
a7aad1c2e8
|
|||
|
76be771215
|
|||
|
4b5aaf3d6f
|
|||
|
6dc82d4a68
|
|||
|
19c8d84e05
|
|||
|
13d53abbdf
|
|||
|
0ab6581fe9
|
|||
|
ba874a77b2
|
|||
|
b26c4ff9e6
|
|||
|
fc2407d10d
|
|||
|
fe391bc678
|
|||
|
6edb856d06
|
|||
|
ae2bdd03a0
|
|||
|
5a91956939
|
|||
|
bd2774b7f9
|
|||
|
fe6b0b3b23
|
|||
|
afc47bf29f
|
|||
|
d4d0f70472
|
|||
|
bb482465fe
|
|||
|
800c867cdd
|
|||
|
4d344e83d3
|
|||
|
9e4221affb
|
|||
|
3c9a864240
|
|||
|
134925a16e
|
|||
|
d460f22a45
|
|||
|
1eb367ac17
|
|||
|
c2dad9fe6d
|
|||
|
124ea12d2e
|
|||
|
b5b05776d8
|
|||
|
faae8e86e0
|
+15
-14
@@ -6,21 +6,17 @@ 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
|
|
||||||
- uses: actions/setup-node@v6
|
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: https://codeberg.org/mlugg/setup-zig@v2
|
||||||
|
|
||||||
- name: Building
|
- name: Building
|
||||||
run: zig build soft
|
run: zig build soft
|
||||||
@@ -28,10 +24,15 @@ jobs:
|
|||||||
- name: Generating docs
|
- name: Generating docs
|
||||||
run: zig build docs
|
run: zig build docs
|
||||||
|
|
||||||
- name: Publish to Cloudflare Pages
|
- name: Deploying docs
|
||||||
uses: cloudflare/wrangler-action@v3
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
uses: milanmk/actions-file-deployer@master
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
remote-protocol: sftp
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
remote-host: ${{ secrets.SFTP_HOST_DOCS }}
|
||||||
command: pages deploy zig-out/docs --project-name=vulkan-driver-docs
|
remote-user: ${{ secrets.SFTP_USER_DOCS }}
|
||||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
remote-password: ${{ secrets.SFTP_PASSWORD_DOCS }}
|
||||||
|
remote-port: 6969
|
||||||
|
local-path: "./zig-out/docs"
|
||||||
|
remote-path: "/www"
|
||||||
|
sync: full
|
||||||
|
|||||||
@@ -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 }}
|
|
||||||
@@ -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
|
||||||
@@ -9,6 +9,7 @@ scripts/__pycache__/
|
|||||||
.gdb_history
|
.gdb_history
|
||||||
*.json
|
*.json
|
||||||
*.png
|
*.png
|
||||||
|
!logo.png
|
||||||
*.bin
|
*.bin
|
||||||
*.qpa
|
*.qpa
|
||||||
*.xml
|
*.xml
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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 = .{
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-20
@@ -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,9 +102,11 @@ 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 (comptime base.config.logs == .verbose) {
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpErrorReturnTrace(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,21 +381,45 @@ 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 subpass = render_pass.interface.subpasses[device.renderer.subpass_index];
|
||||||
|
|
||||||
|
const image_view = blk: {
|
||||||
|
if (impl.attachment.aspect_mask.toInt() == (vk.ImageAspectFlags{ .color_bit = true }).toInt()) {
|
||||||
|
const fb_attachment_index = (subpass.color_attachments orelse return)[impl.attachment.color_attachment].attachment;
|
||||||
|
|
||||||
|
if (fb_attachment_index != vk.ATTACHMENT_UNUSED)
|
||||||
|
break :blk framebuffer.interface.attachments[impl.attachment.color_attachment];
|
||||||
|
} 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 image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.image));
|
||||||
const clear_format = try image.getClearFormat();
|
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(
|
try blitter.clear(
|
||||||
impl.attachment.clear_value,
|
impl.attachment.clear_value,
|
||||||
clear_format,
|
clear_format,
|
||||||
image,
|
image,
|
||||||
image_view.format,
|
image_view.format,
|
||||||
image_view.subresource_range,
|
range,
|
||||||
null,
|
impl.rect.rect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
|
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
|
||||||
@@ -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();
|
||||||
|
|||||||
+238
-13
@@ -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,
|
||||||
|
.storage_buffer,
|
||||||
|
.storage_buffer_dynamic,
|
||||||
|
=> descriptor.* = blk: {
|
||||||
|
const desc: Descriptor = .{
|
||||||
.buffer = local_allocator.alloc(DescriptorBuffer, binding.array_size) catch return VkError.OutOfHostMemory,
|
.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.* = .{
|
|
||||||
|
.storage_image,
|
||||||
|
.input_attachment,
|
||||||
|
=> descriptor.* = blk: {
|
||||||
|
const desc: Descriptor = .{
|
||||||
.image = local_allocator.alloc(DescriptorImage, binding.array_size) catch return VkError.OutOfHostMemory,
|
.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)});
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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,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
@@ -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);
|
||||||
}
|
}
|
||||||
break :blk command_allocator.dupe(u8, "Unkown") catch return VkError.OutOfHostMemory;
|
|
||||||
|
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, 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);
|
||||||
|
}
|
||||||
|
|||||||
+330
-42
@@ -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,19 +84,16 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.* = .{
|
self.stages.put(.compute, blk: {
|
||||||
.interface = interface,
|
|
||||||
.runtimes_allocator = runtimes_allocator_arena,
|
|
||||||
.stages = std.EnumMap(Stages, Shader).init(.{
|
|
||||||
.compute = blk: {
|
|
||||||
var shader: Shader = undefined;
|
var shader: Shader = undefined;
|
||||||
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.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;
|
||||||
|
runtime.rt = spv.Runtime.init(
|
||||||
runtimes_allocator,
|
runtimes_allocator,
|
||||||
&soft_module.module,
|
&soft_module.module,
|
||||||
.{
|
.{
|
||||||
@@ -88,6 +101,8 @@ pub fn createCompute(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)});
|
||||||
@@ -97,7 +112,7 @@ pub fn createCompute(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(
|
runtime.rt.addSpecializationInfo(
|
||||||
runtimes_allocator,
|
runtimes_allocator,
|
||||||
.{
|
.{
|
||||||
.id = @intCast(entry.constant_id),
|
.id = @intCast(entry.constant_id),
|
||||||
@@ -116,9 +131,7 @@ pub fn createCompute(device: *base.Device, allocator: std.mem.Allocator, cache:
|
|||||||
|
|
||||||
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) {
|
||||||
|
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;
|
||||||
|
pixel = blitter.readFloat4(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_view: *SoftImageView = @ptrCast(@alignCast(context));
|
||||||
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
||||||
const pixel = image.readFloat4(
|
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
|
||||||
|
pixel = image.readFloat4(
|
||||||
.{
|
.{
|
||||||
.x = x,
|
.x = x,
|
||||||
.y = y,
|
.y = y,
|
||||||
.z = z,
|
.z = if (dim == .Cube) 0 else z,
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.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 + cube_face,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
image_view.interface.format,
|
||||||
) catch return SpvRuntimeError.Unknown;
|
) 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) {
|
||||||
|
var pixel = @Vector(4, u32){ 0, 0, 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;
|
||||||
|
pixel = blitter.readInt4(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_view: *SoftImageView = @ptrCast(@alignCast(context));
|
||||||
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
||||||
const pixel = image.readInt4(
|
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
|
||||||
|
pixel = image.readInt4(
|
||||||
.{
|
.{
|
||||||
.x = x,
|
.x = x,
|
||||||
.y = y,
|
.y = y,
|
||||||
.z = z,
|
.z = if (dim == .Cube) 0 else z,
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.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 + cube_face,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
image_view.interface.format,
|
||||||
) catch return SpvRuntimeError.Unknown;
|
) catch return SpvRuntimeError.Unknown;
|
||||||
|
}
|
||||||
return .{
|
return .{
|
||||||
.x = pixel[0],
|
.x = pixel[0],
|
||||||
.y = pixel[1],
|
.y = pixel[1],
|
||||||
@@ -284,33 +322,239 @@ 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 vec_pixel = zm.f32x4(pixel.x, pixel.y, pixel.z, pixel.w);
|
||||||
|
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;
|
||||||
|
blitter.writeFloat4(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_view: *SoftImageView = @ptrCast(@alignCast(context));
|
||||||
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
||||||
|
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
|
||||||
image.writeFloat4(
|
image.writeFloat4(
|
||||||
.{
|
.{
|
||||||
.x = x,
|
.x = x,
|
||||||
.y = y,
|
.y = y,
|
||||||
.z = z,
|
.z = if (dim == .Cube) 0 else z,
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.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 + cube_face,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
image_view.interface.format,
|
||||||
.{ pixel.x, pixel.y, pixel.z, pixel.w },
|
vec_pixel,
|
||||||
) catch return SpvRuntimeError.Unknown;
|
) 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 vec_pixel = @Vector(4, u32){ pixel.x, pixel.y, pixel.z, pixel.w };
|
||||||
|
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;
|
||||||
|
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_view: *SoftImageView = @ptrCast(@alignCast(context));
|
||||||
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
const image: *SoftImage = @alignCast(@fieldParentPtr("interface", image_view.interface.image));
|
||||||
|
const cube_face: u32 = if (dim == .Cube) @intCast(z) else 0;
|
||||||
image.writeInt4(
|
image.writeInt4(
|
||||||
.{
|
.{
|
||||||
.x = x,
|
.x = x,
|
||||||
.y = y,
|
.y = y,
|
||||||
.z = z,
|
.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 = if (dim == .Cube)
|
||||||
|
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, image.interface.extent.width - 1)
|
||||||
|
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,
|
||||||
|
.mip_level = image_view.interface.subresource_range.base_mip_level,
|
||||||
|
.array_layer = image_view.interface.subresource_range.base_array_layer + texel.face,
|
||||||
|
},
|
||||||
|
image_view.interface.format,
|
||||||
|
);
|
||||||
|
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,
|
.aspect_mask = image_view.interface.subresource_range.aspect_mask,
|
||||||
@@ -318,6 +562,50 @@ fn writeImageInt4(context: *anyopaque, x: i32, y: i32, z: i32, pixel: spv.Runtim
|
|||||||
.array_layer = image_view.interface.subresource_range.base_array_layer,
|
.array_layer = image_view.interface.subresource_range.base_array_layer,
|
||||||
},
|
},
|
||||||
image_view.interface.format,
|
image_view.interface.format,
|
||||||
.{ pixel.x, pixel.y, pixel.z, pixel.w },
|
|
||||||
) catch return SpvRuntimeError.Unknown;
|
) 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 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -47,9 +47,11 @@ 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 (comptime base.config.logs == .verbose) {
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpErrorReturnTrace(trace);
|
std.debug.dumpErrorReturnTrace(trace);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return VkError.ValidationFailed;
|
return VkError.ValidationFailed;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
#include <cpuinfo.h>
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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,9 +90,11 @@ 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 (comptime base.config.logs == .verbose) {
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpErrorReturnTrace(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,
|
||||||
|
|||||||
@@ -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 => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+145
-257
@@ -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 logger = std.log.scoped(.SoftwareRenderer);
|
const peak_memory_footprint = @divTrunc(bounded_allocator.queryPeakFootprint(), 1000);
|
||||||
if (memory_footprint > 256_000)
|
|
||||||
logger.warn("Drawcall indexed stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint })
|
const fmt =
|
||||||
else
|
\\Drawcall stats:
|
||||||
logger.debug("Drawcall indexed stats:\n> Took {d}us\n> Allocated {d} KB", .{ ms, memory_footprint });
|
\\> 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,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.vertexShaderStage(allocator, &draw_call, index_count, instance_count, 0, first_instance, indices) catch |err| {
|
const logger = std.log.scoped(.SoftwareRenderer);
|
||||||
|
if (memory_footprint > 256_000)
|
||||||
|
logger.warn(fmt, args)
|
||||||
|
else
|
||||||
|
logger.debug(fmt, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (comptime base.config.logs == .verbose) {
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpErrorReturnTrace(trace);
|
std.debug.dumpErrorReturnTrace(trace);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
try self.postVertexDraw(allocator, &draw_call);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
return VkError.Unknown;
|
||||||
_ = self;
|
};
|
||||||
|
|
||||||
|
draw_call.viewport = try self.resolveViewport(0);
|
||||||
|
draw_call.scissor = try self.resolveScissor(0);
|
||||||
|
|
||||||
|
try rasterizer.processThenFragmentStage(self, allocator, &draw_call);
|
||||||
}
|
}
|
||||||
|
|
||||||
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];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn triangleIsCulled(self: *Self, v0: *const Vertex, v1: *const Vertex, v2: *const Vertex) VkError!bool {
|
return VkError.Unknown;
|
||||||
const pipeline_data = (self.state.pipeline orelse return VkError.InvalidHandleDrv).interface.mode.graphics;
|
}
|
||||||
const rasterization = pipeline_data.rasterization;
|
|
||||||
const cull_mode = rasterization.cull_mode;
|
if (pipeline_data.viewport_state.viewports) |viewports| {
|
||||||
|
if (viewport_index < viewports.len)
|
||||||
if (!cull_mode.front_bit and !cull_mode.back_bit)
|
return viewports[viewport_index];
|
||||||
return false;
|
}
|
||||||
|
|
||||||
if (cull_mode.front_bit and cull_mode.back_bit)
|
return VkError.Unknown;
|
||||||
return true;
|
}
|
||||||
|
|
||||||
const area = triangleArea2(v0, v1, v2);
|
fn resolveScissor(self: *Self, scissor_index: usize) VkError!vk.Rect2D {
|
||||||
if (area == 0.0)
|
const pipeline_data =
|
||||||
return true;
|
&(self.state.pipeline orelse return VkError.InvalidPipelineDrv).interface.mode.graphics;
|
||||||
|
|
||||||
const front_face = switch (rasterization.front_face) {
|
if (pipeline_data.dynamic_state.scissor) {
|
||||||
.counter_clockwise => area < 0.0,
|
if (self.dynamic_state.scissor) |scissor| {
|
||||||
.clockwise => area > 0.0,
|
if (scissor_index < scissor.len)
|
||||||
else => return false,
|
return scissor[scissor_index];
|
||||||
};
|
}
|
||||||
|
|
||||||
return (cull_mode.front_bit and front_face) or (cull_mode.back_bit and !front_face);
|
return VkError.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline_data.viewport_state.scissor) |scissor| {
|
||||||
|
if (scissor_index < scissor.len)
|
||||||
|
return scissor[scissor_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return VkError.Unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
+997
-187
File diff suppressed because it is too large
Load Diff
@@ -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(¤t, 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, ¤t, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_inside)
|
||||||
|
try output.append(current);
|
||||||
|
|
||||||
|
previous = current;
|
||||||
|
previous_inside = current_inside;
|
||||||
|
previous_distance = current_distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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 render_target_view: *base.ImageView = draw_call.color_attachments[attachment_ref.attachment];
|
||||||
|
const render_target: *SoftImage = @alignCast(@fieldParentPtr("interface", render_target_view.image));
|
||||||
|
|
||||||
|
const color_range = render_target_view.subresource_range;
|
||||||
|
const color_format = render_target_view.format;
|
||||||
|
|
||||||
|
const color_attachment_subresource_offset = try render_target.getSubresourceOffset(
|
||||||
|
color_range.aspect_mask,
|
||||||
|
color_range.base_mip_level,
|
||||||
|
color_range.base_array_layer,
|
||||||
|
);
|
||||||
|
const color_attachment_subresource_size = render_target.getLayerSize(color_range.aspect_mask);
|
||||||
|
access.* = .{
|
||||||
|
.mutex = undefined,
|
||||||
|
.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),
|
||||||
|
.texel_size = base.format.texelSize(color_format),
|
||||||
|
.format = color_format,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const len = @min(out0.blob.len, out1.blob.len, out2.blob.len);
|
const depth_attachment_view: ?*base.ImageView = if (draw_call.depth_attachment) |view| view else null;
|
||||||
const input = allocator.alloc(u8, len) catch return VkError.OutOfDeviceMemory;
|
const depth_attachment: ?*SoftImage = if (depth_attachment_view) |view| @alignCast(@fieldParentPtr("interface", view.image)) else null;
|
||||||
|
|
||||||
var byte_index: usize = 0;
|
var depth_attachment_access: ?common.RenderTargetAccess = blk: {
|
||||||
while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) {
|
if (depth_attachment == null)
|
||||||
const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]);
|
break :blk null;
|
||||||
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 depth_range = depth_attachment_view.?.subresource_range;
|
||||||
const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]);
|
const depth_format = depth_attachment_view.?.format;
|
||||||
const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]);
|
|
||||||
const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]);
|
|
||||||
writePacked(f32, input[byte_index..], (value0 * b0) + (value1 * b1) + (value2 * b2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byte_index < len)
|
const attachment_subresource_offset = try depth_attachment.?.getSubresourceOffset(
|
||||||
@memcpy(input[byte_index..], out0.blob[byte_index..len]);
|
depth_range.aspect_mask,
|
||||||
|
depth_range.base_mip_level,
|
||||||
inputs[location] = input;
|
depth_range.base_array_layer,
|
||||||
}
|
);
|
||||||
|
const attachment_subresource_size = depth_attachment.?.getLayerSize(depth_range.aspect_mask);
|
||||||
return inputs;
|
break :blk .{
|
||||||
}
|
.mutex = .init,
|
||||||
|
.base = try depth_attachment.?.mapAsSliceWithAddedOffset(u8, attachment_subresource_offset, attachment_subresource_size),
|
||||||
fn interpolateLineOutputs(allocator: std.mem.Allocator, v0: *const Renderer.Vertex, v1: *const Renderer.Vertex, t: f32) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS][]u8 {
|
.row_pitch = depth_attachment.?.getRowPitchMemSizeForMipLevelWithFormat(depth_range.aspect_mask, depth_range.base_mip_level, depth_format),
|
||||||
return interpolateVertexOutputs(allocator, v0, v1, v0, 1.0 - t, t, 0.0);
|
.texel_size = base.format.texelSize(depth_format),
|
||||||
}
|
.format = depth_format,
|
||||||
|
};
|
||||||
pub fn drawLineBresenham(allocator: std.mem.Allocator, fragments: *std.ArrayList(Renderer.Fragment), v0: *Renderer.Vertex, v1: *Renderer.Vertex) VkError!void {
|
|
||||||
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;
|
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];
|
||||||
|
|
||||||
const d_err = @abs(y1 - y0);
|
if ((vertex_index & 1) == 0) {
|
||||||
const d_x = x1 - x0;
|
try clipTransformAndRasterizeTriangle(
|
||||||
const y_step: i32 = if (y0 > y1) -1 else 1;
|
renderer,
|
||||||
|
allocator,
|
||||||
var err = @divTrunc(d_x, 2); // Pixel center.
|
draw_call,
|
||||||
var y = y0;
|
v0,
|
||||||
|
v1,
|
||||||
var x = x0;
|
v2,
|
||||||
while (x <= x1) : (x += 1) {
|
color_attachment_access,
|
||||||
const x_fragment: f32 = @floatFromInt(if (steep) y else x);
|
if (depth_attachment_access) |*access| access else null,
|
||||||
const y_fragment: f32 = @floatFromInt(if (steep) x else y);
|
);
|
||||||
const t = @as(f32, @floatFromInt(x - x0)) / @as(f32, @floatFromInt(@max(d_x, 1)));
|
} else {
|
||||||
|
try clipTransformAndRasterizeTriangle(
|
||||||
const z = ((1.0 - t) * start_vertex.position[2]) + (t * end_vertex.position[2]);
|
renderer,
|
||||||
|
allocator,
|
||||||
fragments.append(allocator, .{
|
draw_call,
|
||||||
.position = zm.f32x4(x_fragment, y_fragment, z, 1.0),
|
v1,
|
||||||
.color = zm.f32x4(1.0, 1.0, 1.0, 1.0),
|
v0,
|
||||||
.inputs = try interpolateLineOutputs(allocator, start_vertex, end_vertex, t),
|
v2,
|
||||||
}) catch return VkError.OutOfDeviceMemory;
|
color_attachment_access,
|
||||||
|
if (depth_attachment_access) |*access| access else null,
|
||||||
err -= @intCast(d_err);
|
);
|
||||||
if (err < 0) {
|
|
||||||
y += y_step;
|
|
||||||
err += d_x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
.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}),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 {
|
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
||||||
return ((p[0] - a[0]) * (b[1] - a[1])) - ((p[1] - a[1]) * (b[0] - a[0]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drawTriangleFilled(allocator: std.mem.Allocator, fragments: *std.ArrayList(Renderer.Fragment), v0: *Renderer.Vertex, v1: *Renderer.Vertex, v2: *Renderer.Vertex) VkError!void {
|
fn clipTransformAndRasterizeLine(
|
||||||
const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0], v2.position[0])));
|
allocator: std.mem.Allocator,
|
||||||
const max_x: i32 = @intFromFloat(@ceil(@max(v0.position[0], v1.position[0], v2.position[0])));
|
draw_call: *DrawCall,
|
||||||
const min_y: i32 = @intFromFloat(@floor(@min(v0.position[1], v1.position[1], v2.position[1])));
|
v0: *Vertex,
|
||||||
const max_y: i32 = @intFromFloat(@ceil(@max(v0.position[1], v1.position[1], v2.position[1])));
|
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;
|
||||||
|
|
||||||
const area = edgeFunction(v0.position, v1.position, v2.position);
|
var tv0 = clipped_line.v0;
|
||||||
if (area == 0.0)
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clipTransformAndRasterizeTriangle(
|
||||||
|
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 {
|
||||||
|
const clipped_polygon = try clip.clipTriangle(allocator, v0, v1, v2);
|
||||||
|
|
||||||
|
if (clipped_polygon.len < 3)
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 (comptime base.config.logs == .verbose) {
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpErrorReturnTrace(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
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
|||||||
@@ -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..]);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user