UI rewrite 3rd attempt
This commit is contained in:
parent
c9fd1b1585
commit
1f5697326c
48 changed files with 2155 additions and 6190 deletions
61
examples/CustomShader/inverse-circle.comp.glsl
Normal file
61
examples/CustomShader/inverse-circle.comp.glsl
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
// Custom UI compute shader. Demonstrates the Tier 1 dispatch path:
|
||||
// the user defines their own item struct, writes their own GLSL alongside
|
||||
// the standard shaders (sharing the same UIDispatchHeader contract via
|
||||
// ui-shared.glsl), and dispatches it via UIRenderer::Dispatch.
|
||||
//
|
||||
// What it does: each item is a circle. For every pixel the workgroup tile
|
||||
// owns, if the pixel falls inside any item-circle, the pixel's RGB is
|
||||
// inverted (1 - rgb). Composes naturally with whatever previous dispatches
|
||||
// drew into the swapchain image — works on top of standard quads, custom
|
||||
// effects, anything.
|
||||
#version 460
|
||||
#extension GL_GOOGLE_include_directive : enable
|
||||
#include "ui-shared.glsl"
|
||||
|
||||
// Application-defined item: just (cx, cy, radius, _pad). Layout matches the
|
||||
// C++ InverseCircleItem struct in main.cpp byte-for-byte under std430.
|
||||
struct InverseCircleItem {
|
||||
vec4 centerRadius;
|
||||
};
|
||||
|
||||
layout(descriptor_heap, std430) readonly buffer InvCircleBuf {
|
||||
InverseCircleItem items[];
|
||||
} invCircleHeap[];
|
||||
|
||||
// NVIDIA workaround — same per-member-load pattern the library shaders use
|
||||
// for the descriptor-heap'd SSBO composite-load issue.
|
||||
InverseCircleItem LoadInverseCircleItem(uint heap, uint i) {
|
||||
InverseCircleItem it;
|
||||
it.centerRadius = invCircleHeap[heap].items[i].centerRadius;
|
||||
return it;
|
||||
}
|
||||
|
||||
layout(push_constant) uniform PC {
|
||||
UIDispatchHeader hdr;
|
||||
} pc;
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
void main() {
|
||||
ivec2 screenPx;
|
||||
if (!uiResolveScreenPixel(pc.hdr, screenPx)) return;
|
||||
|
||||
vec2 sp = vec2(screenPx) + 0.5;
|
||||
|
||||
// Find the strongest circle coverage at this pixel — using max instead
|
||||
// of literal per-item invert so overlapping circles don't double-invert
|
||||
// back to the original.
|
||||
float coverage = 0.0;
|
||||
for (uint i = 0u; i < pc.hdr.itemCount; ++i) {
|
||||
InverseCircleItem it = LoadInverseCircleItem(pc.hdr.itemBuffer, i);
|
||||
vec2 c = it.centerRadius.xy;
|
||||
float r = it.centerRadius.z;
|
||||
float d = length(sp - c) - r;
|
||||
coverage = max(coverage, clamp(0.5 - d, 0.0, 1.0));
|
||||
}
|
||||
if (coverage <= 0.0) return;
|
||||
|
||||
vec4 dst = imageLoad(uiImages[pc.hdr.outImage], screenPx);
|
||||
dst.rgb = mix(dst.rgb, vec3(1.0) - dst.rgb, coverage);
|
||||
imageStore(uiImages[pc.hdr.outImage], screenPx, dst);
|
||||
}
|
||||
105
examples/CustomShader/main.cpp
Normal file
105
examples/CustomShader/main.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
// Tier 1 demo: a user-authored compute shader dispatched alongside the
|
||||
// standard ones. The custom shader inverts RGB in the area covered by a
|
||||
// list of circles. The mouse-tracking circle moves; two static ones sit
|
||||
// on a striped background drawn with the standard DrawQuads shader.
|
||||
|
||||
#include "vulkan/vulkan.h"
|
||||
|
||||
import Crafter.Graphics;
|
||||
import Crafter.Event;
|
||||
import std;
|
||||
using namespace Crafter;
|
||||
|
||||
// Application-side item POD. Matches `struct InverseCircleItem { vec4
|
||||
// centerRadius; }` in inverse-circle.comp.glsl byte-for-byte.
|
||||
struct InverseCircleItem {
|
||||
float cx, cy, radius, _pad;
|
||||
};
|
||||
|
||||
int main() {
|
||||
Device::Initialize();
|
||||
Window window(1280, 720, "Custom Shader");
|
||||
|
||||
VkCommandBuffer init = window.StartInit();
|
||||
|
||||
DescriptorHeapVulkan heap;
|
||||
heap.Initialize(/*images*/ 8, /*buffers*/ 8, /*samplers*/ 4);
|
||||
window.descriptorHeap = &heap;
|
||||
|
||||
UIRenderer ui;
|
||||
ui.Initialize(window, heap, init);
|
||||
window.passes.push_back(&ui);
|
||||
|
||||
// Load the user-authored shader. Same wrapper as the four shipped with
|
||||
// the library — there is no privileged path.
|
||||
ComputeShader inverseCircle;
|
||||
inverseCircle.Load("inverse-circle.comp.spv");
|
||||
|
||||
// User-owned buffers.
|
||||
VulkanBuffer<QuadItem, true> quadsBuf;
|
||||
VulkanBuffer<InverseCircleItem, true> invBuf;
|
||||
quadsBuf.Create(
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 64);
|
||||
invBuf.Create(
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 16);
|
||||
|
||||
auto quadsSlot = ui.RegisterBuffer(quadsBuf);
|
||||
auto invSlot = ui.RegisterBuffer(invBuf);
|
||||
|
||||
EventListener<UIBuildArgs> buildSub(&ui.onBuild, [&](UIBuildArgs a) {
|
||||
VkCommandBuffer cmd = a.cmd;
|
||||
|
||||
Rect canvas = Rect::FromWindow(window);
|
||||
|
||||
// Six vertical stripes covering the canvas — gives the inverse
|
||||
// circles something visibly different to invert.
|
||||
std::array<std::array<float, 4>, 6> palette = {{
|
||||
{0.95f, 0.30f, 0.30f, 1.0f},
|
||||
{0.95f, 0.65f, 0.20f, 1.0f},
|
||||
{0.95f, 0.95f, 0.20f, 1.0f},
|
||||
{0.30f, 0.85f, 0.30f, 1.0f},
|
||||
{0.20f, 0.55f, 0.95f, 1.0f},
|
||||
{0.65f, 0.30f, 0.95f, 1.0f},
|
||||
}};
|
||||
std::uint32_t qc = 0;
|
||||
float stripeW = canvas.w / 6.0f;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
quadsBuf.value[qc++] = QuadItem{
|
||||
i * stripeW, 0, stripeW, canvas.h,
|
||||
palette[i][0], palette[i][1], palette[i][2], palette[i][3],
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Three inverse circles: one tracking the mouse, two stationary.
|
||||
std::uint32_t ic = 0;
|
||||
invBuf.value[ic++] = { window.currentMousePos.x, window.currentMousePos.y, 100.0f, 0.0f };
|
||||
invBuf.value[ic++] = { canvas.w * 0.25f, canvas.h * 0.5f, 60.0f, 0.0f };
|
||||
invBuf.value[ic++] = { canvas.w * 0.75f, canvas.h * 0.5f, 80.0f, 0.0f };
|
||||
|
||||
// Standard dispatch first — paints the stripes.
|
||||
if (qc > 0) {
|
||||
quadsBuf.FlushDevice(cmd, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
ui.DispatchQuads(cmd, quadsSlot, qc);
|
||||
}
|
||||
|
||||
// Custom dispatch second — reads the stripes, inverts under
|
||||
// circles, writes back. The library inserts the inter-dispatch
|
||||
// SHADER_WRITE → SHADER_READ|WRITE barrier automatically.
|
||||
if (ic > 0) {
|
||||
invBuf.FlushDevice(cmd, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
struct PC { UIDispatchHeader hdr; } pc { ui.FillHeader(invSlot, ic) };
|
||||
std::uint32_t gx = (window.width + 7) / 8;
|
||||
std::uint32_t gy = (window.height + 7) / 8;
|
||||
ui.Dispatch(cmd, inverseCircle, &pc, sizeof(pc), gx, gy, 1);
|
||||
}
|
||||
});
|
||||
|
||||
window.FinishInit();
|
||||
window.Render();
|
||||
window.StartUpdate();
|
||||
window.StartSync();
|
||||
}
|
||||
25
examples/CustomShader/project.cpp
Normal file
25
examples/CustomShader/project.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import std;
|
||||
import Crafter.Build;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> args) {
|
||||
Configuration* graphics = LocalProject({
|
||||
.projectFile = "../../project.cpp",
|
||||
.args = std::vector<std::string>(args.begin(), args.end()),
|
||||
});
|
||||
|
||||
Configuration cfg;
|
||||
cfg.path = "./";
|
||||
cfg.name = "CustomShader";
|
||||
cfg.outputName = "CustomShader";
|
||||
ApplyStandardArgs(cfg, args);
|
||||
cfg.dependencies = { graphics };
|
||||
|
||||
std::array<fs::path, 0> ifaces = {};
|
||||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
|
||||
cfg.shaders.emplace_back(fs::path("inverse-circle.comp.glsl"), std::string("main"), ShaderType::Compute);
|
||||
return cfg;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue