Test a blit shader implementation
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
// Prism fullscreen texture blit shader.
|
||||||
|
//
|
||||||
|
// Draws a selected texture from the bindless array as a fullscreen quad that
|
||||||
|
// is letterboxed/pillarboxed to preserve aspect ratio (contain-fit). The NDC
|
||||||
|
// content rect is supplied via push constants so the texture is never
|
||||||
|
// stretched, squashed, or cropped.
|
||||||
|
|
||||||
|
struct BlitData {
|
||||||
|
float4 rect; // NDC fit rect: x0, y0, x1, y1
|
||||||
|
uint selected;
|
||||||
|
uint pad[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
[[vk::push_constant]]
|
||||||
|
BlitData blit;
|
||||||
|
|
||||||
|
Sampler2D textures[];
|
||||||
|
|
||||||
|
struct VSOutput {
|
||||||
|
float4 Pos : SV_POSITION;
|
||||||
|
float2 UV;
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
VSOutput main(uint vertexIndex : SV_VertexID) {
|
||||||
|
VSOutput output;
|
||||||
|
float2 uv = float2(float(vertexIndex & 1), float((vertexIndex >> 1) & 1));
|
||||||
|
output.UV = uv;
|
||||||
|
float2 pos = lerp(blit.rect.xy, blit.rect.zw, uv);
|
||||||
|
output.Pos = float4(pos, 0.0, 1.0);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
float4 main(VSOutput input) {
|
||||||
|
return textures[NonUniformResourceIndex(blit.selected)].Sample(input.UV);
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
/* Copyright (c) 2025-2026, Sascha Willems
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct VSInput {
|
|
||||||
float3 Pos;
|
|
||||||
float3 Normal;
|
|
||||||
float2 UV;
|
|
||||||
};
|
|
||||||
|
|
||||||
Sampler2D textures[];
|
|
||||||
|
|
||||||
struct ShaderData {
|
|
||||||
float4x4 projection;
|
|
||||||
float4x4 view;
|
|
||||||
float4x4 model[3];
|
|
||||||
float4 lightPos;
|
|
||||||
uint32_t selected;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VSOutput {
|
|
||||||
float4 Pos : SV_POSITION;
|
|
||||||
float3 Normal;
|
|
||||||
float2 UV;
|
|
||||||
float3 Factor;
|
|
||||||
float3 LightVec;
|
|
||||||
float3 ViewVec;
|
|
||||||
uint32_t InstanceIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
[shader("vertex")]
|
|
||||||
VSOutput main(VSInput input, uniform ShaderData *shaderData, uint instanceIndex : SV_VulkanInstanceID) {
|
|
||||||
VSOutput output;
|
|
||||||
float4x4 modelMat = shaderData->model[instanceIndex];
|
|
||||||
output.Normal = mul((float3x3)mul(shaderData->view, modelMat), input.Normal);
|
|
||||||
output.UV = input.UV;
|
|
||||||
output.Pos = mul(shaderData->projection, mul(shaderData->view, mul(modelMat, float4(input.Pos.xyz, 1.0))));
|
|
||||||
output.Factor = (shaderData->selected == instanceIndex ? 3.0f : 1.0f);
|
|
||||||
output.InstanceIndex = instanceIndex;
|
|
||||||
// Calculate view vectors required for lighting
|
|
||||||
float4 fragPos = mul(mul(shaderData->view, modelMat), float4(input.Pos.xyz, 1.0));
|
|
||||||
output.LightVec = shaderData->lightPos.xyz - fragPos.xyz;
|
|
||||||
output.ViewVec = -fragPos.xyz;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
[shader("fragment")]
|
|
||||||
float4 main(VSOutput input) {
|
|
||||||
// Phong lighting
|
|
||||||
float3 N = normalize(input.Normal);
|
|
||||||
float3 L = normalize(input.LightVec);
|
|
||||||
float3 V = normalize(input.ViewVec);
|
|
||||||
float3 R = reflect(-L, N);
|
|
||||||
float3 diffuse = max(dot(N, L), 0.0025);
|
|
||||||
float3 specular = pow(max(dot(R, V), 0.0), 16.0) * 0.75;
|
|
||||||
// Sample from texture
|
|
||||||
float3 color = textures[NonUniformResourceIndex(input.InstanceIndex)].Sample(input.UV).rgb * input.Factor;
|
|
||||||
return float4(diffuse * color.rgb + specular, 1.0);
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
# Blender 5.0.1 MTL File: 'suzanne4.blend'
|
|
||||||
# www.blender.org
|
|
||||||
|
|
||||||
newmtl Material
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800000 0.800000 0.800000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl Material.001
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.127605 0.127605 0.127605
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl Material.002
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.000000 0.000000 0.000000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl Material.003
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800000 0.800000 0.800000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl Material.004
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800007 0.200800 0.327761
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl Material.005
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 1.000000 1.000000 1.000000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.500000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
-8150
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,150 @@
|
|||||||
|
# Plan — Fullscreen Texture Blit with Contain-Fit
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Render a texture fullscreen with aspect-ratio preservation. If the texture's
|
||||||
|
aspect ratio does not match the window's, draw it to fit within the window —
|
||||||
|
never stretched, squashed, or cropped. Tall/narrow textures pillarbox (bars on
|
||||||
|
left/right); wide textures letterbox (bars on top/bottom).
|
||||||
|
|
||||||
|
This replaces the Suzanne mesh demo in `main.cpp`, which was for testing the RHI.
|
||||||
|
|
||||||
|
## Fit semantics (contain — never crop)
|
||||||
|
|
||||||
|
```
|
||||||
|
scale = min(win_w / tex_w, win_h / tex_h)
|
||||||
|
content_w = tex_w * scale
|
||||||
|
content_h = tex_h * scale
|
||||||
|
rect = centered: left = (win_w - content_w)/2, top = (win_h - content_h)/2
|
||||||
|
```
|
||||||
|
|
||||||
|
| Texture vs window aspect | Limiting dim | Result |
|
||||||
|
|--------------------------|--------------|-------------------|
|
||||||
|
| equal | — | fills exactly |
|
||||||
|
| wider (e.g. 2048x512) | width | letterbox (T/B) |
|
||||||
|
| taller (e.g. 512x2048) | height | pillarbox (L/R) |
|
||||||
|
|
||||||
|
The fit rect is recomputed per frame from the selected texture's dimensions and
|
||||||
|
the current window size, so window resize works without extra handling.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
### 1. RHI — texture size accessor
|
||||||
|
|
||||||
|
Add a value struct and a by-value getter (matches `prRhiGetSurfaceCapabilities`
|
||||||
|
pattern):
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct PrRhiTextureSize {
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
} PrRhiTextureSize;
|
||||||
|
|
||||||
|
PrRhiTextureSize prRhiGetTextureSize(PrRhiTexture *texture);
|
||||||
|
```
|
||||||
|
|
||||||
|
Files: `pr_rhi_types.h`, `pr_rhi.h`, `vulkan/pr_rhi_vk.h`,
|
||||||
|
`vulkan/pr_rhi_vk.c`, `vulkan/pr_rhi_vk_aliases.h`.
|
||||||
|
|
||||||
|
### 2. Offline shader compilation (drop Slang runtime)
|
||||||
|
|
||||||
|
- New `justfile` `shaders` recipe:
|
||||||
|
`slangc -target spirv -profile spirv_1_4 -o build/shaders/blit.spv assets/blit.slang`
|
||||||
|
(entry points auto-detected from `[shader(...)]` attributes).
|
||||||
|
`build` depends on it.
|
||||||
|
- `main.cpp` loads `build/shaders/blit.spv` via wapp file I/O
|
||||||
|
(`wpFileOpen` / `wpFileGetLength` / `wpFileRead`) into an arena buffer, then
|
||||||
|
`prRhiCreateShader`. No `slang.h` includes, no runtime compilation.
|
||||||
|
- Drop `-lslang` and the `-I .../slang` include from the build.
|
||||||
|
|
||||||
|
### 3. `assets/blit.slang`
|
||||||
|
|
||||||
|
Push constant block (32 bytes — Slang pads structs to 16-byte alignment, so the
|
||||||
|
C++ struct carries explicit `pad[3]` to match):
|
||||||
|
|
||||||
|
```hlsl
|
||||||
|
struct BlitData {
|
||||||
|
float4 rect; // NDC fit rect: x0, y0, x1, y1
|
||||||
|
uint selected;
|
||||||
|
uint pad[3];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- **VS**: generates a 4-vertex triangle-strip quad from `SV_VertexID` (no vertex
|
||||||
|
buffer), maps UV 0–1 into the NDC rect.
|
||||||
|
- **FS**: `textures[NonUniformResourceIndex(selected)].Sample(uv)` — reuses the
|
||||||
|
existing bindless descriptor array.
|
||||||
|
|
||||||
|
### 4. `main.cpp` — clean texture viewer
|
||||||
|
|
||||||
|
Remove everything mesh-related: tinyobj loading, vertex/index buffers,
|
||||||
|
`ShaderData` storage buffers / device addresses, the mesh shader + pipeline,
|
||||||
|
mouse orbit, and the mesh draw. Drop `-ltinyobjloader -lglm` from the link.
|
||||||
|
Remove unused `assets/shader.slang`, `suzanne.obj`, `suzanne.mtl`.
|
||||||
|
|
||||||
|
New flow: RHI init → window/instance/pdev/surface/device/swapchain → load 7
|
||||||
|
textures → bindless descriptor set (variable count 7) → load `blit.spv` →
|
||||||
|
blit pipeline layout (`VERTEX|FRAGMENT` 32-byte push range) → blit pipeline
|
||||||
|
(no vertex input, `TRIANGLE_STRIP`, swapchain color format, no depth,
|
||||||
|
`cull NONE`, dynamic viewport/scissor).
|
||||||
|
|
||||||
|
Render loop per frame:
|
||||||
|
- `compute_fit_rect()` from the selected texture's dims + window size
|
||||||
|
- bind blit pipeline + descriptor set, push `BlitData{ rect, selected }`,
|
||||||
|
`prRhiCmdDraw(cb, 4, 1, 0, 0)`
|
||||||
|
- `+/-` cycles the selected texture; resize recomputes fit automatically
|
||||||
|
|
||||||
|
### 5. Test textures (PIL + `build/bin/toktx`)
|
||||||
|
|
||||||
|
Four generated KTX files in `assets/`, loaded alongside the 3 Suzanne textures
|
||||||
|
(`texture_count = 7`, explicit path array):
|
||||||
|
|
||||||
|
| File | Size | Shows |
|
||||||
|
|-----------------|-----------|--------------------------|
|
||||||
|
| `test_square.ktx` | 1024x1024 | bars on both axes |
|
||||||
|
| `test_fill.ktx` | 1920x1080 | fills the 16:9 window |
|
||||||
|
| `test_wide.ktx` | 2048x512 | letterbox (T/B) |
|
||||||
|
| `test_tall.ktx` | 512x2048 | pillarbox (L/R) |
|
||||||
|
|
||||||
|
Each with distinct gradients + a border grid so any stretch/squash is visible.
|
||||||
|
Generated with `--genmipmap` for mip-aware sampling.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
`just build && just run` — each texture fits without crop/stretch, `+/-` cycles,
|
||||||
|
window resize keeps fit, no Slang runtime in the binary.
|
||||||
|
|
||||||
|
## Verification results (2026-08-08)
|
||||||
|
|
||||||
|
Fit behavior confirmed by capturing the window (X11 driver) and sampling pixels:
|
||||||
|
|
||||||
|
| Texture | Window | Result verified |
|
||||||
|
|---------------------|---------------|------------------------------------------------|
|
||||||
|
| square 1024x1024 | 16:9 wide | pillarbox — pure-black L/R bars, full height |
|
||||||
|
| test_wide 2048x512 | 16:9 wide | letterbox — pure-black T/B bars, full width |
|
||||||
|
| test_tall 512x2048 | 16:9 wide | pillarbox — pure-black L/R bars, full height |
|
||||||
|
| test_fill 1920x1080 | 16:9 wide | fills exactly — no bars anywhere |
|
||||||
|
| square (resized) | portrait | fit recomputed per frame — flips to letterbox |
|
||||||
|
|
||||||
|
Texture cycling (`+/-`, `SDLK_PLUS/KP_PLUS/EQUALS`, `SDLK_MINUS/KP_MINUS`) is a
|
||||||
|
straightforward `selected` bump in the key handler; it was reviewed but not
|
||||||
|
exercise-tested in the headless verification env (KWin/XWayland drops XTEST
|
||||||
|
synthesised keys). The other fit cases were verified by temporarily launching
|
||||||
|
each texture as the initial selection.
|
||||||
|
|
||||||
|
### Bugs found and fixed during verification
|
||||||
|
|
||||||
|
1. **`VK_SUBOPTIMAL_KHR` aborted the app.** `prRhiAcquireNextImageVk` /
|
||||||
|
`prRhiPresentVk` routed `SUBOPTIMAL` into `_checkVk` → `__builtin_trap()`.
|
||||||
|
Both now treat it like `OUT_OF_DATE` (return `PR_RHI_SWAPCHAIN_OUT_OF_DATE`).
|
||||||
|
Triggered immediately under X11/XWayland.
|
||||||
|
2. **Swapchain recreate ignored surface extent.** `prRhiRecreateSwapchainVk`
|
||||||
|
hard-coded the passed width/height; on surfaces whose `currentExtent` is
|
||||||
|
meaningful (X11) that mismatched the drawable and re-looped on SUBOPTIMAL.
|
||||||
|
Now queries `vkGetPhysicalDeviceSurfaceCapabilitiesKHR` and falls back to the
|
||||||
|
passed size only when `currentExtent == 0xFFFFFFFF`.
|
||||||
|
3. **App used logical window size for the swapchain.** Under HiDPI the drawable
|
||||||
|
differs from `SDL_GetWindowSize` (1920x1080 logical → 2400x1350 drawable at
|
||||||
|
1.25x scale), which is the root cause of #1 under XWayland. main.cpp now uses
|
||||||
|
`SDL_GetWindowSizeInPixels` for swapchain width/height, fit math, viewport
|
||||||
|
and scissor.
|
||||||
@@ -13,7 +13,7 @@ VENDOR_INC := BUILDDIR + "/include"
|
|||||||
VENDOR_LIB := BUILDDIR + "/lib"
|
VENDOR_LIB := BUILDDIR + "/lib"
|
||||||
|
|
||||||
VK_FLAGS := "-DPR_RHI_VULKAN -DVK_NO_PROTOTYPES -I" + VK_SDK + "/include -I" + VK_SDK + "/include/vma -I" + VENDOR_INC
|
VK_FLAGS := "-DPR_RHI_VULKAN -DVK_NO_PROTOTYPES -I" + VK_SDK + "/include -I" + VK_SDK + "/include/vma -I" + VENDOR_INC
|
||||||
APP_INC := "-I" + VK_SDK + "/include -I" + VK_SDK + "/include/vma -I" + VK_SDK + "/include/slang -I" + VENDOR_INC + " -Isrc"
|
APP_INC := "-I" + VK_SDK + "/include -I" + VK_SDK + "/include/vma -I" + VENDOR_INC + " -Isrc"
|
||||||
|
|
||||||
# Build KTX from source
|
# Build KTX from source
|
||||||
vendor:
|
vendor:
|
||||||
@@ -30,8 +30,14 @@ vendor:
|
|||||||
cmake --build {{BUILDDIR}}/ktx --config Release
|
cmake --build {{BUILDDIR}}/ktx --config Release
|
||||||
cmake --install {{BUILDDIR}}/ktx
|
cmake --install {{BUILDDIR}}/ktx
|
||||||
|
|
||||||
|
# Compile shaders from .slang to SPIR-V
|
||||||
|
shaders:
|
||||||
|
mkdir -p {{BUILDDIR}}/shaders
|
||||||
|
{{VK_SDK}}/bin/slangc -target spirv -profile spirv_1_4 \
|
||||||
|
-o {{BUILDDIR}}/shaders/blit.spv assets/blit.slang
|
||||||
|
|
||||||
# Build all objects, then link
|
# Build all objects, then link
|
||||||
build: vendor
|
build: vendor shaders
|
||||||
mkdir -p {{BUILDDIR}}/bin
|
mkdir -p {{BUILDDIR}}/bin
|
||||||
bear -- {{CXX}} -g -c -Wno-nullability-completeness {{VK_FLAGS}} \
|
bear -- {{CXX}} -g -c -Wno-nullability-completeness {{VK_FLAGS}} \
|
||||||
src/prism/rhi/vulkan/profiles/vulkan_profiles.cpp \
|
src/prism/rhi/vulkan/profiles/vulkan_profiles.cpp \
|
||||||
@@ -50,7 +56,7 @@ build: vendor
|
|||||||
bear -a -- {{CXX}} -g {{VK_FLAGS}} \
|
bear -a -- {{CXX}} -g {{VK_FLAGS}} \
|
||||||
-L{{VK_SDK}}/lib -L{{VENDOR_LIB}} \
|
-L{{VK_SDK}}/lib -L{{VENDOR_LIB}} \
|
||||||
build/*.o \
|
build/*.o \
|
||||||
-lSDL3 -lglm -ltinyobjloader -lktx -lslang -lvulkan \
|
-lSDL3 -lktx -lvulkan \
|
||||||
-Wl,-rpath,{{VENDOR_LIB}} -Wl,-rpath,{{VK_SDK}}/lib \
|
-Wl,-rpath,{{VENDOR_LIB}} -Wl,-rpath,{{VK_SDK}}/lib \
|
||||||
-o {{BUILDDIR}}/bin/prism
|
-o {{BUILDDIR}}/bin/prism
|
||||||
@echo "--- build done: {{BUILDDIR}}/bin/prism ---"
|
@echo "--- build done: {{BUILDDIR}}/bin/prism ---"
|
||||||
|
|||||||
+143
-252
@@ -1,26 +1,18 @@
|
|||||||
// vim:fileencoding=utf-8:foldmethod=marker
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
//
|
//
|
||||||
// Prism port of how-to-vulkan's main.cpp — uses the RHI API instead of
|
// Prism texture viewer — draws a texture fullscreen with contain-fit
|
||||||
// direct Vulkan calls.
|
// (letterbox/pillarbox to preserve aspect ratio, never crops or stretches).
|
||||||
|
// Texture selection cycles with +/-.
|
||||||
|
|
||||||
#include "prism/rhi/pr_rhi_types.h"
|
#include "prism/rhi/pr_rhi_types.h"
|
||||||
#include "prism/rhi/pr_rhi.h"
|
#include "prism/rhi/pr_rhi.h"
|
||||||
#include <SDL3/SDL_timer.h>
|
#include <SDL3/SDL_timer.h>
|
||||||
#include <glm/ext/matrix_clip_space.hpp>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <SDL3/SDL_events.h>
|
#include <SDL3/SDL_events.h>
|
||||||
#include <SDL3/SDL_init.h>
|
#include <SDL3/SDL_init.h>
|
||||||
#include <SDL3/SDL_keycode.h>
|
#include <SDL3/SDL_keycode.h>
|
||||||
#include <SDL3/SDL_video.h>
|
#include <SDL3/SDL_video.h>
|
||||||
#include <slang/slang.h>
|
|
||||||
#include <slang/slang-com-ptr.h>
|
|
||||||
#include <tiny_obj_loader.h>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Exit codes
|
// Exit codes
|
||||||
@@ -29,18 +21,12 @@
|
|||||||
enum ExitCode {
|
enum ExitCode {
|
||||||
EXIT_CODE_SUCCESS,
|
EXIT_CODE_SUCCESS,
|
||||||
EXIT_CODE_SDL_INIT_FAILED,
|
EXIT_CODE_SDL_INIT_FAILED,
|
||||||
EXIT_CODE_VULKAN_LIB_LOAD_FAILED,
|
|
||||||
EXIT_CODE_WINDOW_CREATION_FAILED,
|
EXIT_CODE_WINDOW_CREATION_FAILED,
|
||||||
EXIT_CODE_SURFACE_CREATION_FAILED,
|
|
||||||
EXIT_CODE_GET_WINDOW_SIZE_FAILED,
|
EXIT_CODE_GET_WINDOW_SIZE_FAILED,
|
||||||
EXIT_CODE_NO_INSTANCE_SUPPORT,
|
|
||||||
EXIT_CODE_NO_PHYSICAL_DEVICES,
|
EXIT_CODE_NO_PHYSICAL_DEVICES,
|
||||||
EXIT_CODE_ALLOCATION_FAILURE,
|
|
||||||
EXIT_CODE_NO_SUITABLE_PHYSICAL_DEVICE,
|
EXIT_CODE_NO_SUITABLE_PHYSICAL_DEVICE,
|
||||||
EXIT_CODE_NO_PRESENTATION_SUPPORT,
|
EXIT_CODE_ALLOCATION_FAILURE,
|
||||||
EXIT_CODE_NO_SUITABLE_DEPTH_FORMAT,
|
EXIT_CODE_SHADER_LOAD_FAILED,
|
||||||
EXIT_CODE_MESH_LOAD_FAILED,
|
|
||||||
EXIT_CODE_SYNC_OBJ_CREATE_FAILED,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void check(bool result, i32 code) {
|
static inline void check(bool result, i32 code) {
|
||||||
@@ -54,39 +40,39 @@ static inline void check(bool result, i32 code) {
|
|||||||
// Types
|
// Types
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
struct Vertex {
|
|
||||||
glm::vec3 pos;
|
|
||||||
glm::vec3 normal;
|
|
||||||
glm::vec2 uv;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ShaderData {
|
|
||||||
glm::mat4 projection;
|
|
||||||
glm::mat4 view;
|
|
||||||
glm::mat4 model[3];
|
|
||||||
glm::vec4 light_pos{ 0.0f, -10.0f, 10.0f, 0.0f };
|
|
||||||
u32 selected{ 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureResources {
|
struct TextureResources {
|
||||||
PrRhiTexture *texture;
|
PrRhiTexture *texture;
|
||||||
PrRhiSampler *sampler;
|
PrRhiSampler *sampler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Push constant block — matches BlitData in assets/blit.slang (16-byte aligned,
|
||||||
|
// so the struct is padded to 32 bytes).
|
||||||
|
struct BlitData {
|
||||||
|
f32 rect[4]; // NDC fit rect: x0, y0, x1, y1
|
||||||
|
u32 selected;
|
||||||
|
u32 pad[3];
|
||||||
|
};
|
||||||
|
|
||||||
// Typedefs for wapp arrays of our types
|
// Typedefs for wapp arrays of our types
|
||||||
typedef Vertex *VertexArray;
|
typedef TextureResources *TextureResourcesArray;
|
||||||
typedef u16 *U16Array;
|
typedef PrRhiSemaphore **PrRhiSemaphoreArray;
|
||||||
typedef glm::vec3 *GlmVec3Array;
|
typedef PrRhiFence **PrRhiFenceArray;
|
||||||
typedef TextureResources *TextureResourcesArray;
|
typedef PrRhiCommandBuffer **PrRhiCommandBufferArray;
|
||||||
typedef PrRhiBuffer **PrRhiBufferArray;
|
|
||||||
typedef PrRhiFence **PrRhiFenceArray;
|
|
||||||
typedef PrRhiSemaphore **PrRhiSemaphoreArray;
|
|
||||||
typedef PrRhiCommandBuffer **PrRhiCommandBufferArray;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Global state
|
// Global state
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
static const char *const TEXTURE_PATHS[] = {
|
||||||
|
"assets/suzanne0.ktx",
|
||||||
|
"assets/suzanne1.ktx",
|
||||||
|
"assets/suzanne2.ktx",
|
||||||
|
"assets/test_square.ktx",
|
||||||
|
"assets/test_fill.ktx",
|
||||||
|
"assets/test_wide.ktx",
|
||||||
|
"assets/test_tall.ktx",
|
||||||
|
};
|
||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
PrRhiInstance *inst;
|
PrRhiInstance *inst;
|
||||||
PrRhiPhysicalDevice *pdev;
|
PrRhiPhysicalDevice *pdev;
|
||||||
@@ -96,40 +82,62 @@ struct AppState {
|
|||||||
|
|
||||||
PrRhiFormat swapchain_format;
|
PrRhiFormat swapchain_format;
|
||||||
|
|
||||||
PrRhiBuffer *vert_index_buf;
|
|
||||||
u64 vertex_buf_size;
|
|
||||||
u64 index_count;
|
|
||||||
|
|
||||||
static constexpr u32 max_frames_in_flight = 2;
|
static constexpr u32 max_frames_in_flight = 2;
|
||||||
static constexpr u32 instance_count = 3;
|
static constexpr u32 texture_count = (u32)(sizeof(TEXTURE_PATHS) / sizeof(TEXTURE_PATHS[0]));
|
||||||
static constexpr u32 texture_count = 3;
|
|
||||||
|
|
||||||
PrRhiBufferArray shader_data_bufs;
|
PrRhiFenceArray fences;
|
||||||
PrRhiFenceArray fences;
|
PrRhiSemaphoreArray image_acquired_semaphores;
|
||||||
PrRhiSemaphoreArray image_acquired_semaphores;
|
PrRhiSemaphoreArray render_completed_semaphores;
|
||||||
PrRhiSemaphoreArray render_completed_semaphores;
|
u32 render_semaphore_count;
|
||||||
PrRhiCommandPool *cmd_pool;
|
PrRhiCommandPool *cmd_pool;
|
||||||
PrRhiCommandBufferArray cmd_buffers;
|
PrRhiCommandBufferArray cmd_buffers;
|
||||||
|
|
||||||
TextureResourcesArray textures;
|
TextureResourcesArray textures;
|
||||||
PrRhiDescriptorSetLayout *desc_set_layout;
|
PrRhiDescriptorSetLayout *desc_set_layout;
|
||||||
PrRhiDescriptorPool *desc_pool;
|
PrRhiDescriptorPool *desc_pool;
|
||||||
PrRhiDescriptorSet *desc_set;
|
PrRhiDescriptorSet *desc_set;
|
||||||
|
|
||||||
PrRhiShader *shader;
|
PrRhiShader *shader;
|
||||||
PrRhiPipelineLayout *pipeline_layout;
|
PrRhiPipelineLayout *pipeline_layout;
|
||||||
PrRhiPipeline *pipeline;
|
PrRhiPipeline *pipeline;
|
||||||
|
|
||||||
ShaderData shader_data;
|
u32 selected;
|
||||||
u32 frame_index;
|
u32 frame_index;
|
||||||
glm::ivec2 window_size;
|
i32 window_width;
|
||||||
GlmVec3Array object_rotations;
|
i32 window_height;
|
||||||
bool update_swapchain;
|
bool update_swapchain;
|
||||||
|
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
Slang::ComPtr<slang::IGlobalSession> slang_session;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Helpers
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
// Fills rect with the NDC bounds (x0, y0, x1, y1) of the texture content when
|
||||||
|
// contained within the window, preserving aspect ratio and centering.
|
||||||
|
static void computeFitRect(f32 tex_w, f32 tex_h, f32 win_w, f32 win_h, f32 *rect) {
|
||||||
|
if (win_w <= 0.0f || win_h <= 0.0f || tex_w <= 0.0f || tex_h <= 0.0f) {
|
||||||
|
rect[0] = -1.0f;
|
||||||
|
rect[1] = -1.0f;
|
||||||
|
rect[2] = 1.0f;
|
||||||
|
rect[3] = 1.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 scale_x = win_w / tex_w;
|
||||||
|
f32 scale_y = win_h / tex_h;
|
||||||
|
f32 scale = scale_x < scale_y ? scale_x : scale_y;
|
||||||
|
|
||||||
|
f32 content_w = tex_w * scale;
|
||||||
|
f32 content_h = tex_h * scale;
|
||||||
|
|
||||||
|
rect[0] = -content_w / win_w;
|
||||||
|
rect[1] = -content_h / win_h;
|
||||||
|
rect[2] = content_w / win_w;
|
||||||
|
rect[3] = content_h / win_h;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Main
|
// Main
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -144,7 +152,7 @@ int main() {
|
|||||||
check(SDL_Init(SDL_INIT_VIDEO), EXIT_CODE_SDL_INIT_FAILED);
|
check(SDL_Init(SDL_INIT_VIDEO), EXIT_CODE_SDL_INIT_FAILED);
|
||||||
|
|
||||||
f32 display_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
|
f32 display_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
|
||||||
app.window = SDL_CreateWindow("How To Vulkan (Prism)", (i32)(display_scale * 1920),
|
app.window = SDL_CreateWindow("Prism — Texture Viewer", (i32)(display_scale * 1920),
|
||||||
(i32)(display_scale * 1080),
|
(i32)(display_scale * 1080),
|
||||||
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
|
SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE);
|
||||||
check(app.window != nullptr, EXIT_CODE_WINDOW_CREATION_FAILED);
|
check(app.window != nullptr, EXIT_CODE_WINDOW_CREATION_FAILED);
|
||||||
@@ -185,7 +193,7 @@ int main() {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Surface creation
|
// {{{ Surface creation
|
||||||
check(SDL_GetWindowSize(app.window, &app.window_size.x, &app.window_size.y),
|
check(SDL_GetWindowSizeInPixels(app.window, &app.window_width, &app.window_height),
|
||||||
EXIT_CODE_GET_WINDOW_SIZE_FAILED);
|
EXIT_CODE_GET_WINDOW_SIZE_FAILED);
|
||||||
app.surface = prRhiCreateSurfaceFromWindow(app.inst, app.window);
|
app.surface = prRhiCreateSurfaceFromWindow(app.inst, app.window);
|
||||||
// }}}
|
// }}}
|
||||||
@@ -200,8 +208,8 @@ int main() {
|
|||||||
// {{{ Swapchain creation
|
// {{{ Swapchain creation
|
||||||
PrRhiSwapchainDesc swap_desc = {};
|
PrRhiSwapchainDesc swap_desc = {};
|
||||||
swap_desc.surface = app.surface;
|
swap_desc.surface = app.surface;
|
||||||
swap_desc.width = (u32)app.window_size.x;
|
swap_desc.width = (u32)app.window_width;
|
||||||
swap_desc.height = (u32)app.window_size.y;
|
swap_desc.height = (u32)app.window_height;
|
||||||
swap_desc.has_depth = true;
|
swap_desc.has_depth = true;
|
||||||
swap_desc.depth_format = PR_RHI_FORMAT_D24_UNORM_S8_UINT;
|
swap_desc.depth_format = PR_RHI_FORMAT_D24_UNORM_S8_UINT;
|
||||||
|
|
||||||
@@ -209,77 +217,7 @@ int main() {
|
|||||||
app.swapchain_format = prRhiGetSwapchainFormat(app.swapchain);
|
app.swapchain_format = prRhiGetSwapchainFormat(app.swapchain);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Vertex/Index buffers
|
|
||||||
// {{{ Load mesh
|
|
||||||
tinyobj::attrib_t attrib;
|
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
|
||||||
std::vector<tinyobj::material_t> materials;
|
|
||||||
|
|
||||||
check(tinyobj::LoadObj(&attrib, &shapes, &materials, nullptr, nullptr, "assets/suzanne.obj"),
|
|
||||||
EXIT_CODE_MESH_LOAD_FAILED);
|
|
||||||
|
|
||||||
VertexArray vertices = wpArrayAllocCapacity(Vertex, &arena, 128, WP_ARRAY_INIT_NONE);
|
|
||||||
U16Array indices = wpArrayAllocCapacity(u16, &arena, 128, WP_ARRAY_INIT_NONE);
|
|
||||||
|
|
||||||
for (auto &idx : shapes[0].mesh.indices) {
|
|
||||||
Vertex v = {};
|
|
||||||
v.pos = {
|
|
||||||
attrib.vertices[idx.vertex_index * 3],
|
|
||||||
-attrib.vertices[idx.vertex_index * 3 + 1],
|
|
||||||
attrib.vertices[idx.vertex_index * 3 + 2]
|
|
||||||
};
|
|
||||||
v.normal = {
|
|
||||||
attrib.normals[idx.normal_index * 3],
|
|
||||||
-attrib.normals[idx.normal_index * 3 + 1],
|
|
||||||
attrib.normals[idx.normal_index * 3 + 2]
|
|
||||||
};
|
|
||||||
v.uv = {
|
|
||||||
attrib.texcoords[idx.texcoord_index * 2],
|
|
||||||
1.0f - attrib.texcoords[idx.texcoord_index * 2 + 1]
|
|
||||||
};
|
|
||||||
|
|
||||||
u16 index = (u16)wpArrayCount(indices);
|
|
||||||
vertices = wpArrayAppendAlloc(Vertex, &arena, vertices, &v, WP_ARRAY_INIT_NONE);
|
|
||||||
indices = wpArrayAppendAlloc(u16, &arena, indices, &index, WP_ARRAY_INIT_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.vertex_buf_size = sizeof(Vertex) * wpArrayCount(vertices);
|
|
||||||
app.index_count = wpArrayCount(indices);
|
|
||||||
u64 index_buf_size = sizeof(u16) * app.index_count;
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// {{{ Create GPU buffer
|
|
||||||
PrRhiBufferDesc vert_desc = {};
|
|
||||||
vert_desc.size = app.vertex_buf_size + index_buf_size;
|
|
||||||
vert_desc.usage = (PrRhiBufferUsage)(PR_RHI_BUFFER_USAGE_VERTEX | PR_RHI_BUFFER_USAGE_INDEX);
|
|
||||||
vert_desc.memory = PR_RHI_MEMORY_CPU_TO_GPU;
|
|
||||||
|
|
||||||
app.vert_index_buf = prRhiCreateBuffer(app.device, vert_desc);
|
|
||||||
|
|
||||||
void *mapped = prRhiBufferMap(app.device, app.vert_index_buf);
|
|
||||||
memcpy(mapped, vertices, app.vertex_buf_size);
|
|
||||||
memcpy((u8 *)mapped + app.vertex_buf_size, indices, index_buf_size);
|
|
||||||
prRhiBufferUnmap(app.device, app.vert_index_buf);
|
|
||||||
// }}}
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// {{{ Shader data buffers
|
|
||||||
app.shader_data_bufs = wpArrayAllocCapacity(PrRhiBuffer *, &arena,
|
|
||||||
AppState::max_frames_in_flight,
|
|
||||||
WP_ARRAY_INIT_FILLED);
|
|
||||||
for (u32 i = 0; i < AppState::max_frames_in_flight; ++i) {
|
|
||||||
PrRhiBufferDesc buf_desc = {};
|
|
||||||
buf_desc.size = sizeof(ShaderData);
|
|
||||||
buf_desc.usage = (PrRhiBufferUsage)(PR_RHI_BUFFER_USAGE_STORAGE |
|
|
||||||
PR_RHI_BUFFER_USAGE_SHADER_DEVICE_ADDRESS);
|
|
||||||
buf_desc.memory = PR_RHI_MEMORY_CPU_TO_GPU;
|
|
||||||
|
|
||||||
app.shader_data_bufs[i] = prRhiCreateBuffer(app.device, buf_desc);
|
|
||||||
}
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// {{{ Synchronisation objects
|
// {{{ Synchronisation objects
|
||||||
// Fences
|
|
||||||
app.fences = wpArrayAllocCapacity(PrRhiFence *, &arena, AppState::max_frames_in_flight,
|
app.fences = wpArrayAllocCapacity(PrRhiFence *, &arena, AppState::max_frames_in_flight,
|
||||||
WP_ARRAY_INIT_FILLED);
|
WP_ARRAY_INIT_FILLED);
|
||||||
for (u32 i = 0; i < AppState::max_frames_in_flight; ++i) {
|
for (u32 i = 0; i < AppState::max_frames_in_flight; ++i) {
|
||||||
@@ -288,7 +226,6 @@ int main() {
|
|||||||
app.fences[i] = prRhiCreateFence(app.device, fd);
|
app.fences[i] = prRhiCreateFence(app.device, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image acquired semaphores (per frame)
|
|
||||||
app.image_acquired_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
app.image_acquired_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
||||||
AppState::max_frames_in_flight,
|
AppState::max_frames_in_flight,
|
||||||
WP_ARRAY_INIT_FILLED);
|
WP_ARRAY_INIT_FILLED);
|
||||||
@@ -296,12 +233,11 @@ int main() {
|
|||||||
app.image_acquired_semaphores[i] = prRhiCreateSemaphore(app.device);
|
app.image_acquired_semaphores[i] = prRhiCreateSemaphore(app.device);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render completed semaphores (per swapchain image)
|
app.render_semaphore_count = prRhiGetSwapchainImageCount(app.swapchain);
|
||||||
u32 swapchain_image_count = prRhiGetSwapchainImageCount(app.swapchain);
|
|
||||||
app.render_completed_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
app.render_completed_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
||||||
swapchain_image_count,
|
app.render_semaphore_count,
|
||||||
WP_ARRAY_INIT_FILLED);
|
WP_ARRAY_INIT_FILLED);
|
||||||
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
for (u32 i = 0; i < app.render_semaphore_count; ++i) {
|
||||||
app.render_completed_semaphores[i] = prRhiCreateSemaphore(app.device);
|
app.render_completed_semaphores[i] = prRhiCreateSemaphore(app.device);
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
@@ -320,11 +256,8 @@ int main() {
|
|||||||
PrRhiCommandBuffer *upload_cb = upload_cbs[0];
|
PrRhiCommandBuffer *upload_cb = upload_cbs[0];
|
||||||
|
|
||||||
for (u32 i = 0; i < AppState::texture_count; ++i) {
|
for (u32 i = 0; i < AppState::texture_count; ++i) {
|
||||||
char buf[2048] = {};
|
PrRhiTexture *tex = prRhiCreateTextureFromKtx(app.device, TEXTURE_PATHS[i], app.cmd_pool, upload_cb);
|
||||||
snprintf(buf, sizeof(buf), "assets/suzanne%u.ktx", i);
|
|
||||||
PrRhiTexture *tex = prRhiCreateTextureFromKtx(app.device, buf, app.cmd_pool, upload_cb);
|
|
||||||
|
|
||||||
// Create sampler
|
|
||||||
PrRhiSamplerDesc samp_desc = {};
|
PrRhiSamplerDesc samp_desc = {};
|
||||||
samp_desc.mag_filter = PR_RHI_FILTER_LINEAR;
|
samp_desc.mag_filter = PR_RHI_FILTER_LINEAR;
|
||||||
samp_desc.min_filter = PR_RHI_FILTER_LINEAR;
|
samp_desc.min_filter = PR_RHI_FILTER_LINEAR;
|
||||||
@@ -355,7 +288,6 @@ int main() {
|
|||||||
PrRhiDescriptorSetLayoutDesc layout_desc = { ds_layouts };
|
PrRhiDescriptorSetLayoutDesc layout_desc = { ds_layouts };
|
||||||
app.desc_set_layout = prRhiCreateDescriptorSetLayout(app.device, layout_desc);
|
app.desc_set_layout = prRhiCreateDescriptorSetLayout(app.device, layout_desc);
|
||||||
|
|
||||||
// Pool
|
|
||||||
PrRhiDescriptorPoolSize pool_size = {};
|
PrRhiDescriptorPoolSize pool_size = {};
|
||||||
pool_size.type = PR_RHI_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
pool_size.type = PR_RHI_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
pool_size.descriptor_count = AppState::texture_count;
|
pool_size.descriptor_count = AppState::texture_count;
|
||||||
@@ -366,12 +298,10 @@ int main() {
|
|||||||
|
|
||||||
app.desc_pool = prRhiCreateDescriptorPool(app.device, ds_pool_desc);
|
app.desc_pool = prRhiCreateDescriptorPool(app.device, ds_pool_desc);
|
||||||
|
|
||||||
// Allocate descriptor set (variable count)
|
|
||||||
WpU32Array var_counts = wpArray(u32, AppState::texture_count);
|
WpU32Array var_counts = wpArray(u32, AppState::texture_count);
|
||||||
app.desc_set = prRhiAllocateDescriptorSet(app.device, app.desc_pool, app.desc_set_layout,
|
app.desc_set = prRhiAllocateDescriptorSet(app.device, app.desc_pool, app.desc_set_layout,
|
||||||
var_counts);
|
var_counts);
|
||||||
|
|
||||||
// Write descriptor set
|
|
||||||
PrRhiDescriptorImageInfoArray img_infos = wpArrayAllocCapacity(PrRhiDescriptorImageInfo,
|
PrRhiDescriptorImageInfoArray img_infos = wpArrayAllocCapacity(PrRhiDescriptorImageInfo,
|
||||||
&arena,
|
&arena,
|
||||||
AppState::texture_count,
|
AppState::texture_count,
|
||||||
@@ -394,39 +324,29 @@ int main() {
|
|||||||
prRhiUpdateDescriptorSet(app.device, writes);
|
prRhiUpdateDescriptorSet(app.device, writes);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Shader compilation (Slang)
|
// {{{ Load blit shader (pre-compiled SPIR-V)
|
||||||
slang::createGlobalSession(app.slang_session.writeRef());
|
WpStr8RO spirv_path = wpStr8LitRo("build/shaders/blit.spv");
|
||||||
|
WpFile *spirv_file = wpFileOpen(&arena, &spirv_path, WP_ACCESS_READ);
|
||||||
|
check(spirv_file != nullptr, EXIT_CODE_SHADER_LOAD_FAILED);
|
||||||
|
|
||||||
slang::TargetDesc target = {};
|
i64 spirv_len = wpFileGetLength(spirv_file);
|
||||||
target.format = SLANG_SPIRV;
|
void *spirv = wpMemAllocatorAlloc(&arena, (u64)spirv_len);
|
||||||
target.profile = {app.slang_session->findProfile("spirv_1_4")};
|
check(spirv != nullptr, EXIT_CODE_ALLOCATION_FAILURE);
|
||||||
|
wpFileRead(spirv, spirv_file, (u64)spirv_len);
|
||||||
|
wpFileClose(spirv_file);
|
||||||
|
|
||||||
Slang::ComPtr<slang::ISession> slang_session;
|
|
||||||
slang::SessionDesc session_desc = {};
|
|
||||||
session_desc.targets = ⌖
|
|
||||||
session_desc.targetCount = 1;
|
|
||||||
session_desc.defaultMatrixLayoutMode = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR;
|
|
||||||
app.slang_session->createSession(session_desc, slang_session.writeRef());
|
|
||||||
|
|
||||||
Slang::ComPtr<slang::IModule> slang_module {
|
|
||||||
slang_session->loadModuleFromSource("triangle", "assets/shader.slang", nullptr, nullptr),
|
|
||||||
};
|
|
||||||
Slang::ComPtr<slang::IBlob> spirv;
|
|
||||||
slang_module->getTargetCode(0, spirv.writeRef());
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// {{{ Create shader module
|
|
||||||
PrRhiShaderDesc shader_desc = {};
|
PrRhiShaderDesc shader_desc = {};
|
||||||
shader_desc.spirv_code = spirv->getBufferPointer();
|
shader_desc.spirv_code = spirv;
|
||||||
shader_desc.spirv_size = spirv->getBufferSize();
|
shader_desc.spirv_size = (u64)spirv_len;
|
||||||
|
|
||||||
app.shader = prRhiCreateShader(app.device, shader_desc);
|
app.shader = prRhiCreateShader(app.device, shader_desc);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Pipeline layout
|
// {{{ Pipeline layout
|
||||||
PrRhiPushConstantRange pc_range = {};
|
PrRhiPushConstantRange pc_range = {};
|
||||||
pc_range.stage_flags = PR_RHI_SHADER_STAGE_VERTEX;
|
pc_range.stage_flags = (PrRhiShaderStage)(PR_RHI_SHADER_STAGE_VERTEX |
|
||||||
pc_range.size = sizeof(u64);
|
PR_RHI_SHADER_STAGE_FRAGMENT);
|
||||||
|
pc_range.size = sizeof(BlitData);
|
||||||
|
|
||||||
PrRhiDescriptorSetLayoutArray pl_layouts = wpArray(PrRhiDescriptorSetLayout *, app.desc_set_layout);
|
PrRhiDescriptorSetLayoutArray pl_layouts = wpArray(PrRhiDescriptorSetLayout *, app.desc_set_layout);
|
||||||
|
|
||||||
@@ -438,18 +358,6 @@ int main() {
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Graphics pipeline
|
// {{{ Graphics pipeline
|
||||||
PrRhiVertexInputBindingArray vertex_bindings = wpArray(
|
|
||||||
PrRhiVertexInputBinding,
|
|
||||||
PrRhiVertexInputBinding{ 0, sizeof(Vertex) }
|
|
||||||
);
|
|
||||||
|
|
||||||
PrRhiVertexAttributeArray vertex_attrs = wpArray(
|
|
||||||
PrRhiVertexAttribute,
|
|
||||||
PrRhiVertexAttribute{ 0, 0, PR_RHI_FORMAT_R32G32B32_SFLOAT, 0 },
|
|
||||||
PrRhiVertexAttribute{ 1, 0, PR_RHI_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) },
|
|
||||||
PrRhiVertexAttribute{ 2, 0, PR_RHI_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) }
|
|
||||||
);
|
|
||||||
|
|
||||||
PrRhiColorBlendAttachmentArray blend_attachments = wpArray(
|
PrRhiColorBlendAttachmentArray blend_attachments = wpArray(
|
||||||
PrRhiColorBlendAttachment,
|
PrRhiColorBlendAttachment,
|
||||||
PrRhiColorBlendAttachment{ 0xf }
|
PrRhiColorBlendAttachment{ 0xf }
|
||||||
@@ -457,37 +365,34 @@ int main() {
|
|||||||
|
|
||||||
PrRhiFormatArray color_fmt_array = wpArray(PrRhiFormat, app.swapchain_format);
|
PrRhiFormatArray color_fmt_array = wpArray(PrRhiFormat, app.swapchain_format);
|
||||||
|
|
||||||
PrRhiGraphicsPipelineDesc pipe_desc = {};
|
PrRhiGraphicsPipelineDesc pipe_desc = {};
|
||||||
pipe_desc.vertex_shader = app.shader;
|
pipe_desc.vertex_shader = app.shader;
|
||||||
pipe_desc.vertex_shader_entry_point = "main";
|
pipe_desc.vertex_shader_entry_point = "main";
|
||||||
pipe_desc.fragment_shader = app.shader;
|
pipe_desc.fragment_shader = app.shader;
|
||||||
pipe_desc.fragment_shader_entry_point = "main";
|
pipe_desc.fragment_shader_entry_point = "main";
|
||||||
pipe_desc.vertex_bindings = vertex_bindings;
|
pipe_desc.topology = PR_RHI_TOPOLOGY_TRIANGLE_STRIP;
|
||||||
pipe_desc.vertex_attributes = vertex_attrs;
|
|
||||||
pipe_desc.topology = PR_RHI_TOPOLOGY_TRIANGLE_LIST;
|
|
||||||
pipe_desc.color_attachment_formats = color_fmt_array;
|
pipe_desc.color_attachment_formats = color_fmt_array;
|
||||||
pipe_desc.depth_attachment_format = swap_desc.depth_format;
|
pipe_desc.depth_attachment_format = swap_desc.depth_format;
|
||||||
pipe_desc.depth_test_enable = true;
|
pipe_desc.depth_test_enable = false;
|
||||||
pipe_desc.depth_write_enable = true;
|
pipe_desc.depth_write_enable = false;
|
||||||
pipe_desc.depth_compare_op = PR_RHI_COMPARE_OP_LESS_OR_EQUAL;
|
|
||||||
pipe_desc.blend_attachments = blend_attachments;
|
pipe_desc.blend_attachments = blend_attachments;
|
||||||
pipe_desc.dynamic_viewport = true;
|
pipe_desc.dynamic_viewport = true;
|
||||||
pipe_desc.dynamic_scissor = true;
|
pipe_desc.dynamic_scissor = true;
|
||||||
pipe_desc.cull_mode = PR_RHI_CULL_MODE_BACK;
|
pipe_desc.cull_mode = PR_RHI_CULL_MODE_NONE;
|
||||||
pipe_desc.front_face = PR_RHI_FRONT_FACE_COUNTER_CLOCKWISE;
|
|
||||||
pipe_desc.line_width = 1.0f;
|
pipe_desc.line_width = 1.0f;
|
||||||
|
pipe_desc.multisample_count = PR_RHI_SAMPLE_COUNT_1;
|
||||||
pipe_desc.layout = app.pipeline_layout;
|
pipe_desc.layout = app.pipeline_layout;
|
||||||
|
|
||||||
app.pipeline = prRhiCreateGraphicsPipeline(app.device, pipe_desc);
|
app.pipeline = prRhiCreateGraphicsPipeline(app.device, pipe_desc);
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Render loop
|
// {{{ Render loop
|
||||||
app.object_rotations = wpArrayAllocCapacity(glm::vec3, &arena, AppState::instance_count,
|
|
||||||
WP_ARRAY_INIT_FILLED);
|
|
||||||
u64 last_time = SDL_GetTicks();
|
|
||||||
SDL_Event event = {};
|
|
||||||
app.frame_index = 0;
|
app.frame_index = 0;
|
||||||
|
app.selected = 0;
|
||||||
u32 image_index = 0;
|
u32 image_index = 0;
|
||||||
|
PrRhiTextureSize tex_size = prRhiGetTextureSize(app.textures[app.selected].texture);
|
||||||
|
std::cout << "Texture " << app.selected << ": " << TEXTURE_PATHS[app.selected]
|
||||||
|
<< " (" << tex_size.width << 'x' << tex_size.height << ")\n";
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// {{{ Wait on fence
|
// {{{ Wait on fence
|
||||||
@@ -508,20 +413,11 @@ int main() {
|
|||||||
if (app.update_swapchain) {
|
if (app.update_swapchain) {
|
||||||
// Skip this frame — will recreate below
|
// Skip this frame — will recreate below
|
||||||
} else {
|
} else {
|
||||||
// {{{ Update shader data
|
// {{{ Compute contain-fit rect
|
||||||
app.shader_data.projection = glm::perspective(glm::radians(45.0f),
|
PrRhiTextureSize tex_size = prRhiGetTextureSize(app.textures[app.selected].texture);
|
||||||
(f32)app.window_size.x / (f32)app.window_size.y,
|
f32 fit_rect[4] = {};
|
||||||
0.1f, 32.0f);
|
computeFitRect((f32)tex_size.width, (f32)tex_size.height,
|
||||||
app.shader_data.view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -6.0f));
|
(f32)app.window_width, (f32)app.window_height, fit_rect);
|
||||||
for (i32 i = 0; i < (i32)AppState::instance_count; ++i) {
|
|
||||||
glm::vec3 instance_pos = glm::vec3((f32)(i - 1) * 3.0f, 0.0f, 0.0f);
|
|
||||||
app.shader_data.model[i] = glm::translate(glm::mat4(1.0f), instance_pos) *
|
|
||||||
glm::mat4_cast(glm::quat(app.object_rotations[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
void *shader_data_ptr = prRhiBufferMap(app.device, app.shader_data_bufs[app.frame_index]);
|
|
||||||
memcpy(shader_data_ptr, &app.shader_data, sizeof(ShaderData));
|
|
||||||
prRhiBufferUnmap(app.device, app.shader_data_bufs[app.frame_index]);
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// {{{ Record command buffer
|
// {{{ Record command buffer
|
||||||
@@ -534,7 +430,6 @@ int main() {
|
|||||||
PrRhiImageMemoryBarrierArray barriers_arr =
|
PrRhiImageMemoryBarrierArray barriers_arr =
|
||||||
wpArrayWithCapacity(PrRhiImageMemoryBarrier, 2, WP_ARRAY_INIT_FILLED);
|
wpArrayWithCapacity(PrRhiImageMemoryBarrier, 2, WP_ARRAY_INIT_FILLED);
|
||||||
|
|
||||||
// Color attachment
|
|
||||||
PrRhiTexture *color_tex = prRhiGetSwapchainTexture(app.swapchain, image_index);
|
PrRhiTexture *color_tex = prRhiGetSwapchainTexture(app.swapchain, image_index);
|
||||||
barriers_arr[0].texture = color_tex;
|
barriers_arr[0].texture = color_tex;
|
||||||
barriers_arr[0].old_layout = PR_RHI_LAYOUT_UNDEFINED;
|
barriers_arr[0].old_layout = PR_RHI_LAYOUT_UNDEFINED;
|
||||||
@@ -544,7 +439,6 @@ int main() {
|
|||||||
barriers_arr[0].dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
|
barriers_arr[0].dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
|
||||||
barriers_arr[0].dst_access_mask = (PrRhiAccess)(PR_RHI_ACCESS_COLOR_ATTACHMENT_READ | PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE);
|
barriers_arr[0].dst_access_mask = (PrRhiAccess)(PR_RHI_ACCESS_COLOR_ATTACHMENT_READ | PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE);
|
||||||
|
|
||||||
// Depth attachment
|
|
||||||
PrRhiTexture *depth_tex = prRhiGetSwapchainDepthTexture(app.swapchain);
|
PrRhiTexture *depth_tex = prRhiGetSwapchainDepthTexture(app.swapchain);
|
||||||
barriers_arr[1].texture = depth_tex;
|
barriers_arr[1].texture = depth_tex;
|
||||||
barriers_arr[1].old_layout = PR_RHI_LAYOUT_UNDEFINED;
|
barriers_arr[1].old_layout = PR_RHI_LAYOUT_UNDEFINED;
|
||||||
@@ -578,8 +472,8 @@ int main() {
|
|||||||
prRhiCmdBeginRendering(cb, color_arr, &depth_att);
|
prRhiCmdBeginRendering(cb, color_arr, &depth_att);
|
||||||
}
|
}
|
||||||
|
|
||||||
prRhiCmdSetViewport(cb, 0.0f, 0.0f, (f32)app.window_size.x, (f32)app.window_size.y);
|
prRhiCmdSetViewport(cb, 0.0f, 0.0f, (f32)app.window_width, (f32)app.window_height);
|
||||||
prRhiCmdSetScissor(cb, 0, 0, (u32)app.window_size.x, (u32)app.window_size.y);
|
prRhiCmdSetScissor(cb, 0, 0, (u32)app.window_width, (u32)app.window_height);
|
||||||
|
|
||||||
prRhiCmdBindPipeline(cb, PR_RHI_PIPELINE_BIND_POINT_GRAPHICS, app.pipeline);
|
prRhiCmdBindPipeline(cb, PR_RHI_PIPELINE_BIND_POINT_GRAPHICS, app.pipeline);
|
||||||
|
|
||||||
@@ -587,16 +481,18 @@ int main() {
|
|||||||
prRhiCmdBindDescriptorSets(cb, PR_RHI_PIPELINE_BIND_POINT_GRAPHICS,
|
prRhiCmdBindDescriptorSets(cb, PR_RHI_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
app.pipeline_layout, 0, sets);
|
app.pipeline_layout, 0, sets);
|
||||||
|
|
||||||
PrRhiBufferArray vert_buf_arr = wpArray(PrRhiBuffer *, app.vert_index_buf);
|
BlitData blit = {};
|
||||||
WpU64Array vert_offsets = wpArray(u64, 0);
|
blit.rect[0] = fit_rect[0];
|
||||||
prRhiCmdBindVertexBuffers(cb, 0, vert_buf_arr, vert_offsets);
|
blit.rect[1] = fit_rect[1];
|
||||||
prRhiCmdBindIndexBuffer(cb, app.vert_index_buf, app.vertex_buf_size, PR_RHI_INDEX_TYPE_UINT16);
|
blit.rect[2] = fit_rect[2];
|
||||||
|
blit.rect[3] = fit_rect[3];
|
||||||
|
blit.selected = app.selected;
|
||||||
|
prRhiCmdPushConstants(cb, app.pipeline_layout,
|
||||||
|
(PrRhiShaderStage)(PR_RHI_SHADER_STAGE_VERTEX |
|
||||||
|
PR_RHI_SHADER_STAGE_FRAGMENT),
|
||||||
|
0, sizeof(BlitData), &blit);
|
||||||
|
|
||||||
// Push shader data buffer device address
|
prRhiCmdDraw(cb, 4, 1, 0, 0);
|
||||||
u64 buf_addr = prRhiGetBufferDeviceAddress(app.device, app.shader_data_bufs[app.frame_index]);
|
|
||||||
prRhiCmdPushConstants(cb, app.pipeline_layout, PR_RHI_SHADER_STAGE_VERTEX, 0, sizeof(u64), &buf_addr);
|
|
||||||
|
|
||||||
prRhiCmdDrawIndexed(cb, (u32)app.index_count, AppState::instance_count, 0, 0, 0);
|
|
||||||
prRhiCmdEndRendering(cb);
|
prRhiCmdEndRendering(cb);
|
||||||
|
|
||||||
// Transition to present
|
// Transition to present
|
||||||
@@ -632,33 +528,28 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// {{{ Poll events
|
// {{{ Poll events
|
||||||
f32 elapsed_time = (SDL_GetTicks() - last_time) / 1000.0f;
|
SDL_Event event = {};
|
||||||
last_time = SDL_GetTicks();
|
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
app.update_swapchain = false; // signal exit — use update_swapchain flag
|
|
||||||
goto done;
|
goto done;
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
if (event.key.key == SDLK_ESCAPE) goto done;
|
if (event.key.key == SDLK_ESCAPE) goto done;
|
||||||
if (event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS || event.key.key == SDLK_EQUALS) {
|
if (event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS || event.key.key == SDLK_EQUALS) {
|
||||||
app.shader_data.selected = (app.shader_data.selected < 2) ? app.shader_data.selected + 1 : 0;
|
app.selected = (app.selected + 1) % AppState::texture_count;
|
||||||
|
PrRhiTextureSize ts = prRhiGetTextureSize(app.textures[app.selected].texture);
|
||||||
|
std::cout << "Texture " << app.selected << ": " << TEXTURE_PATHS[app.selected]
|
||||||
|
<< " (" << ts.width << 'x' << ts.height << ")\n";
|
||||||
}
|
}
|
||||||
if (event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS) {
|
if (event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS) {
|
||||||
app.shader_data.selected = (app.shader_data.selected > 0) ? app.shader_data.selected - 1 : 2;
|
app.selected = (app.selected + AppState::texture_count - 1) % AppState::texture_count;
|
||||||
|
PrRhiTextureSize ts = prRhiGetTextureSize(app.textures[app.selected].texture);
|
||||||
|
std::cout << "Texture " << app.selected << ": " << TEXTURE_PATHS[app.selected]
|
||||||
|
<< " (" << ts.width << 'x' << ts.height << ")\n";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_MOUSE_MOTION:
|
|
||||||
if (event.button.button == SDL_BUTTON_LEFT) {
|
|
||||||
app.object_rotations[app.shader_data.selected].x -= (f32)event.motion.yrel * elapsed_time;
|
|
||||||
app.object_rotations[app.shader_data.selected].y += (f32)event.motion.xrel * elapsed_time;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_MOUSE_WHEEL:
|
|
||||||
// Camera position handled via shader_data update in loop
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_WINDOW_RESIZED:
|
case SDL_EVENT_WINDOW_RESIZED:
|
||||||
check(SDL_GetWindowSize(app.window, &app.window_size.x, &app.window_size.y),
|
check(SDL_GetWindowSizeInPixels(app.window, &app.window_width, &app.window_height),
|
||||||
EXIT_CODE_GET_WINDOW_SIZE_FAILED);
|
EXIT_CODE_GET_WINDOW_SIZE_FAILED);
|
||||||
app.update_swapchain = true;
|
app.update_swapchain = true;
|
||||||
break;
|
break;
|
||||||
@@ -670,12 +561,14 @@ int main() {
|
|||||||
if (app.update_swapchain) {
|
if (app.update_swapchain) {
|
||||||
prRhiDeviceWaitIdle(app.device);
|
prRhiDeviceWaitIdle(app.device);
|
||||||
prRhiRecreateSwapchain(app.device, &app.swapchain,
|
prRhiRecreateSwapchain(app.device, &app.swapchain,
|
||||||
(u32)app.window_size.x, (u32)app.window_size.y);
|
(u32)app.window_width, (u32)app.window_height);
|
||||||
|
|
||||||
// Re-create render completed semaphores for new image count
|
// Re-create render completed semaphores for the new image count
|
||||||
// TODO: proper cleanup — for now, just leak old ones
|
u32 new_count = prRhiGetSwapchainImageCount(app.swapchain);
|
||||||
u32 new_count = 0;
|
for (u32 i = 0; i < app.render_semaphore_count; ++i) {
|
||||||
prRhiAcquireNextImage(app.device, app.swapchain, NULL, &new_count);
|
prRhiDestroySemaphore(app.device, app.render_completed_semaphores[i]);
|
||||||
|
}
|
||||||
|
app.render_semaphore_count = new_count;
|
||||||
app.render_completed_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
app.render_completed_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
|
||||||
new_count, WP_ARRAY_INIT_FILLED);
|
new_count, WP_ARRAY_INIT_FILLED);
|
||||||
for (u32 i = 0; i < new_count; ++i) {
|
for (u32 i = 0; i < new_count; ++i) {
|
||||||
@@ -708,15 +601,13 @@ int main() {
|
|||||||
prRhiFreeCommandBuffers(app.device, app.cmd_pool, app.cmd_buffers);
|
prRhiFreeCommandBuffers(app.device, app.cmd_pool, app.cmd_buffers);
|
||||||
prRhiDestroyCommandPool(app.device, app.cmd_pool);
|
prRhiDestroyCommandPool(app.device, app.cmd_pool);
|
||||||
|
|
||||||
for (u32 i = 0; i < swapchain_image_count; ++i) {
|
for (u32 i = 0; i < app.render_semaphore_count; ++i) {
|
||||||
prRhiDestroySemaphore(app.device, app.render_completed_semaphores[i]);
|
prRhiDestroySemaphore(app.device, app.render_completed_semaphores[i]);
|
||||||
}
|
}
|
||||||
for (u32 i = 0; i < AppState::max_frames_in_flight; ++i) {
|
for (u32 i = 0; i < AppState::max_frames_in_flight; ++i) {
|
||||||
prRhiDestroySemaphore(app.device, app.image_acquired_semaphores[i]);
|
prRhiDestroySemaphore(app.device, app.image_acquired_semaphores[i]);
|
||||||
prRhiDestroyFence(app.device, app.fences[i]);
|
prRhiDestroyFence(app.device, app.fences[i]);
|
||||||
prRhiDestroyBuffer(app.device, app.shader_data_bufs[i]);
|
|
||||||
}
|
}
|
||||||
prRhiDestroyBuffer(app.device, app.vert_index_buf);
|
|
||||||
|
|
||||||
prRhiDestroySwapchain(app.device, app.swapchain);
|
prRhiDestroySwapchain(app.device, app.swapchain);
|
||||||
prRhiDestroyDevice(app.device);
|
prRhiDestroyDevice(app.device);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ PrRhiTexture *prRhiCreateTexture(PrRhiDevice *device, PrRhiTextureDesc desc);
|
|||||||
PrRhiTexture *prRhiCreateTextureFromKtx(PrRhiDevice *device, const char *path,
|
PrRhiTexture *prRhiCreateTextureFromKtx(PrRhiDevice *device, const char *path,
|
||||||
PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
|
PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
|
||||||
void prRhiDestroyTexture(PrRhiDevice *device, PrRhiTexture *texture);
|
void prRhiDestroyTexture(PrRhiDevice *device, PrRhiTexture *texture);
|
||||||
|
PrRhiTextureSize prRhiGetTextureSize(PrRhiTexture *texture);
|
||||||
|
|
||||||
// ======================================================================
|
// ======================================================================
|
||||||
// Samplers
|
// Samplers
|
||||||
|
|||||||
@@ -352,6 +352,11 @@ typedef struct PrRhiShaderDesc {
|
|||||||
u64 spirv_size;
|
u64 spirv_size;
|
||||||
} PrRhiShaderDesc;
|
} PrRhiShaderDesc;
|
||||||
|
|
||||||
|
typedef struct PrRhiTextureSize {
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
} PrRhiTextureSize;
|
||||||
|
|
||||||
typedef struct PrRhiPipelineLayoutDesc {
|
typedef struct PrRhiPipelineLayoutDesc {
|
||||||
PrRhiDescriptorSetLayoutArray set_layouts;
|
PrRhiDescriptorSetLayoutArray set_layouts;
|
||||||
PrRhiPushConstantRangeArray push_constant_ranges;
|
PrRhiPushConstantRangeArray push_constant_ranges;
|
||||||
|
|||||||
@@ -834,7 +834,9 @@ PrRhiSwapchainResult prRhiAcquireNextImageVk(PrRhiDevice *device, PrRhiSwapchain
|
|||||||
VkSemaphore vk_semaphore = signal_semaphore ? signal_semaphore->handle : VK_NULL_HANDLE;
|
VkSemaphore vk_semaphore = signal_semaphore ? signal_semaphore->handle : VK_NULL_HANDLE;
|
||||||
VkResult res = vkAcquireNextImageKHR(device->handle, swapchain->handle, UINT64_MAX, vk_semaphore,
|
VkResult res = vkAcquireNextImageKHR(device->handle, swapchain->handle, UINT64_MAX, vk_semaphore,
|
||||||
VK_NULL_HANDLE, &swapchain->current_image_index);
|
VK_NULL_HANDLE, &swapchain->current_image_index);
|
||||||
if (res == VK_ERROR_OUT_OF_DATE_KHR) { return PR_RHI_SWAPCHAIN_OUT_OF_DATE; }
|
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
|
||||||
|
return PR_RHI_SWAPCHAIN_OUT_OF_DATE;
|
||||||
|
}
|
||||||
_checkVk(res, "vkAcquireNextImageKHR");
|
_checkVk(res, "vkAcquireNextImageKHR");
|
||||||
if (out_image_index) { *out_image_index = swapchain->current_image_index; }
|
if (out_image_index) { *out_image_index = swapchain->current_image_index; }
|
||||||
return PR_RHI_SWAPCHAIN_SUCCESS;
|
return PR_RHI_SWAPCHAIN_SUCCESS;
|
||||||
@@ -854,7 +856,9 @@ PrRhiSwapchainResult prRhiPresentVk(PrRhiDevice *device, PrRhiSwapchain *swapcha
|
|||||||
};
|
};
|
||||||
|
|
||||||
VkResult res = vkQueuePresentKHR(device->queue, &present_info);
|
VkResult res = vkQueuePresentKHR(device->queue, &present_info);
|
||||||
if (res == VK_ERROR_OUT_OF_DATE_KHR) { return PR_RHI_SWAPCHAIN_OUT_OF_DATE; }
|
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
|
||||||
|
return PR_RHI_SWAPCHAIN_OUT_OF_DATE;
|
||||||
|
}
|
||||||
_checkVk(res, "vkQueuePresentKHR");
|
_checkVk(res, "vkQueuePresentKHR");
|
||||||
return PR_RHI_SWAPCHAIN_SUCCESS;
|
return PR_RHI_SWAPCHAIN_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -866,13 +870,22 @@ void prRhiRecreateSwapchainVk(PrRhiDevice *device, PrRhiSwapchain **swapchain,
|
|||||||
|
|
||||||
VkDevice vk_device = device->handle;
|
VkDevice vk_device = device->handle;
|
||||||
VkPhysicalDevice vk_pdev = device->physical_device;
|
VkPhysicalDevice vk_pdev = device->physical_device;
|
||||||
|
VkSurfaceKHR vk_surface = ((PrRhiSurface *)old->surface)->handle;
|
||||||
|
|
||||||
VkExtent2D extent = { width, height };
|
VkSurfaceCapabilitiesKHR caps = {};
|
||||||
|
_checkVk(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_pdev, vk_surface, &caps),
|
||||||
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
|
||||||
|
|
||||||
|
VkExtent2D extent = caps.currentExtent;
|
||||||
|
if (extent.width == 0xffffffff) {
|
||||||
|
extent.width = width;
|
||||||
|
extent.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
VkSwapchainCreateInfoKHR swapchain_info = {
|
VkSwapchainCreateInfoKHR swapchain_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
.oldSwapchain = old->handle,
|
.oldSwapchain = old->handle,
|
||||||
.surface = ((PrRhiSurface *)old->surface)->handle,
|
.surface = vk_surface,
|
||||||
.minImageCount = old->image_count,
|
.minImageCount = old->image_count,
|
||||||
.imageFormat = (VkFormat)old->format,
|
.imageFormat = (VkFormat)old->format,
|
||||||
.imageColorSpace = _default_colorspace,
|
.imageColorSpace = _default_colorspace,
|
||||||
@@ -1315,6 +1328,14 @@ PrRhiTexture *prRhiCreateTextureFromKtxVk(PrRhiDevice *device, const char *path,
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrRhiTextureSize prRhiGetTextureSizeVk(PrRhiTexture *texture) {
|
||||||
|
PrRhiTextureSize size = {};
|
||||||
|
if (!texture) { return size; }
|
||||||
|
size.width = texture->width;
|
||||||
|
size.height = texture->height;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
void prRhiDestroyTextureVk(PrRhiDevice *device, PrRhiTexture *texture) {
|
void prRhiDestroyTextureVk(PrRhiDevice *device, PrRhiTexture *texture) {
|
||||||
if (!texture) { return; }
|
if (!texture) { return; }
|
||||||
VkDevice vk_device = device->handle;
|
VkDevice vk_device = device->handle;
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ PrRhiTexture *prRhiCreateTextureVk(PrRhiDevice *device, PrRhiTextureDesc desc);
|
|||||||
PrRhiTexture *prRhiCreateTextureFromKtxVk(PrRhiDevice *device, const char *path,
|
PrRhiTexture *prRhiCreateTextureFromKtxVk(PrRhiDevice *device, const char *path,
|
||||||
PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
|
PrRhiCommandPool *pool, PrRhiCommandBuffer *cb);
|
||||||
void prRhiDestroyTextureVk(PrRhiDevice *device, PrRhiTexture *texture);
|
void prRhiDestroyTextureVk(PrRhiDevice *device, PrRhiTexture *texture);
|
||||||
|
PrRhiTextureSize prRhiGetTextureSizeVk(PrRhiTexture *texture);
|
||||||
|
|
||||||
PrRhiSampler *prRhiCreateSamplerVk(PrRhiDevice *device, PrRhiSamplerDesc desc);
|
PrRhiSampler *prRhiCreateSamplerVk(PrRhiDevice *device, PrRhiSamplerDesc desc);
|
||||||
void prRhiDestroySamplerVk(PrRhiDevice *device, PrRhiSampler *sampler);
|
void prRhiDestroySamplerVk(PrRhiDevice *device, PrRhiSampler *sampler);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#define prRhiCreateTexture prRhiCreateTextureVk
|
#define prRhiCreateTexture prRhiCreateTextureVk
|
||||||
#define prRhiCreateTextureFromKtx prRhiCreateTextureFromKtxVk
|
#define prRhiCreateTextureFromKtx prRhiCreateTextureFromKtxVk
|
||||||
#define prRhiDestroyTexture prRhiDestroyTextureVk
|
#define prRhiDestroyTexture prRhiDestroyTextureVk
|
||||||
|
#define prRhiGetTextureSize prRhiGetTextureSizeVk
|
||||||
#define prRhiCreateSampler prRhiCreateSamplerVk
|
#define prRhiCreateSampler prRhiCreateSamplerVk
|
||||||
#define prRhiDestroySampler prRhiDestroySamplerVk
|
#define prRhiDestroySampler prRhiDestroySamplerVk
|
||||||
#define prRhiCreateShader prRhiCreateShaderVk
|
#define prRhiCreateShader prRhiCreateShaderVk
|
||||||
|
|||||||
Reference in New Issue
Block a user