12wgpu::ShaderModule primitiveShader(wgpu::Device &
device,
const char *
source) {
13 wgpu::ShaderSourceWGSL wgsl{};
23 WGPUTextureFormat format, uint32_t sampleCount) {
24 const uint64_t
key =
static_cast<uint64_t
>(
depth) | (
static_cast<uint64_t
>(
blend) << 2u) |
25 (
static_cast<uint64_t
>(cull) << 5u) | (
static_cast<uint64_t
>(uint32_t(format)) << 8u) |
26 (
static_cast<uint64_t
>(sampleCount) << 40u);
27 if (
auto found = primitive3DPipelines.find(
key);
found != primitive3DPipelines.end())
return found->second;
29 static constexpr const char *kPrimitiveVert = R
"wgsl(
30struct VSIn { @location(0) clipPosition: vec4f, @location(1) color: vec4f };
31struct VSOut { @builtin(position) position: vec4f, @location(0) color: vec4f };
32@vertex fn vs_main(input: VSIn) -> VSOut {
34 output.position = input.clipPosition;
35 // Match the Vulkan-convention clip coordinates used by the shared resolver.
36 output.position.y = -output.position.y;
37 output.color = input.color;
41 static constexpr const char *kPrimitiveFrag = R
"wgsl(
42@fragment fn fs_main(@location(0) color: vec4f) -> @location(0) vec4f { return color; }
44 wgpu::VertexAttribute attributes[2]{};
45 attributes[0].format = wgpu::VertexFormat::Float32x4;
46 attributes[0].offset = offsetof(Primitive3DVertex, clipPosition);
47 attributes[0].shaderLocation = 0;
48 attributes[1].format = wgpu::VertexFormat::Float32x4;
49 attributes[1].offset = offsetof(Primitive3DVertex, color);
50 attributes[1].shaderLocation = 1;
51 PipelineBuilder builder;
52 builder.label("eve_primitive3d")
53 .vertexLayout(
sizeof(Primitive3DVertex), wgpu::VertexStepMode::Vertex, attributes, 2)
54 .shader(primitiveShader(device, kPrimitiveVert),
"vs_main", primitiveShader(device, kPrimitiveFrag),
"fs_main")
55 .colorTarget(format,
blend)
56 .depth(WGPUTextureFormat_Depth32Float,
60 .sampleCount(sampleCount)
61 .frontFace(wgpu::FrontFace::CCW)
63 ? wgpu::CullMode::
Back
65 : static_cast<wgpu::CullMode>(WGPUCullMode_None)));
66 wgpu::RenderPipeline pipeline = builder.build(device);
67 primitive3DPipelines.emplace(
key, pipeline);
72 if (!frame3DStarted)
throw Exception(
"drawPrimitiveScene: call begin3DFrame first");
74 if (resolved.
vertices.empty())
return;
76 const std::size_t firstSequence = primitive3DDraws.size();
77 primitive3DDraws.reserve(firstSequence + resolved.
batches3D.size());
80 draw.paint = batch.paint;
81 draw.averageDepth = batch.averageDepth;
82 draw.sequence = firstSequence + batch.sequence;
83 draw.vertices.reserve(batch.vertexCount);
84 for (std::size_t vertex = batch.firstVertex; vertex < batch.firstVertex + batch.vertexCount; ++vertex)
85 draw.vertices.push_back({resolved.
vertices[vertex].clipPosition, resolved.
vertices[vertex].color});
86 primitive3DDraws.push_back(std::move(
draw));
90void Graphics::flushPrimitive3D(wgpu::RenderPassEncoder pass, WGPUTextureFormat format, uint32_t sampleCount) {
91 if (primitive3DDraws.empty())
return;
92 std::stable_sort(primitive3DDraws.begin(), primitive3DDraws.end(),
93 [](
const Primitive3DDraw &
a,
const Primitive3DDraw &
b) {
94 const bool ignoreA = a.paint.depth == PrimitiveDepthMode::Ignore;
95 const bool ignoreB = b.paint.depth == PrimitiveDepthMode::Ignore;
96 if (ignoreA != ignoreB) return !ignoreA;
97 if (a.paint.layer != b.paint.layer) return a.paint.layer < b.paint.layer;
98 const bool transparentA = a.paint.blend != BlendMode::Opaque || a.paint.color.a < 1.f;
99 const bool transparentB = b.paint.blend != BlendMode::Opaque || b.paint.color.a < 1.f;
100 if (transparentA != transparentB) return !transparentA;
101 if (transparentA && a.averageDepth != b.averageDepth) return a.averageDepth > b.averageDepth;
102 return a.sequence < b.sequence;
104 auto &arena = currentVertexArena();
105 std::size_t totalBytes = 0;
106 for (
const Primitive3DDraw &
draw : primitive3DDraws) totalBytes +=
draw.
vertices.
size() * sizeof(Primitive3DVertex);
107 ensureVertexArena(arena, arena.used + totalBytes);
108 std::vector<Primitive3DVertex>
vertices;
109 vertices.reserve(totalBytes /
sizeof(Primitive3DVertex));
110 for (
const Primitive3DDraw &
draw : primitive3DDraws)
112 const uint64_t
offset = arena.alloc(totalBytes);
113 queue.WriteBuffer(arena.buffer,
offset,
vertices.data(), totalBytes);
114 pass.SetVertexBuffer(0, arena.buffer,
offset, totalBytes);
116 for (
const Primitive3DDraw &
draw : primitive3DDraws) {
117 if (
draw.vertices.empty())
continue;
119 getPrimitive3DPipeline(
draw.paint.depth,
draw.paint.blend,
draw.paint.cull, format, sampleCount));
120 pass.Draw(
static_cast<uint32_t
>(
draw.vertices.size()), 1,
firstVertex, 0);
123 primitive3DDraws.clear();
std::uint32_t firstVertex
std::vector< Point > vertices
const UnitySourceAsset & source
EVENGINE_API_FOUNDATION public API.
Frame-local scene recorder for 3D line primitives.
void drawPrimitiveScene(const PrimitiveSceneCanvas3D &canvas) override
Draws primitive scene.
PrimitiveCullMode
Face culling used by filled 3D primitives.
eve::BlendMode BlendMode
Compatibility alias for the shared 2D blend mode.
ResolvedPrimitiveTriangles resolvePrimitiveStrokes3D(const PrimitiveSceneCanvas3D &canvas)
Resolves 3D stroke segment bodies with near-plane clipping.
PrimitiveDepthMode
Depth behavior of one 3D primitive.
One contiguous 3D pipeline/sort batch in the resolved vertex stream.
Owning backend-neutral triangle stream for one primitive pass.
std::vector< PrimitiveTriangleVertex > vertices
std::vector< ResolvedPrimitiveBatch3D > batches3D