载入中...
搜索中...
未找到
GraphicsInternal.h
浏览该文件的文档.
1#pragma once
2
3// Shared implementation helpers for the Vulkan graphics backend.
4// Re-generated from the merged dev single-TU Graphics.cpp (pure move).
5// Anonymous namespace: each TU gets its own internal copy.
6
8#include "common/Exception.h"
10
11#include <algorithm>
12#include <cmath>
13#include <cstdint>
14#include <cstdio>
15#include <cstdlib>
16#include <cstring>
17#include <memory>
18#include <stdexcept>
19#include <string>
20#include <utility>
21#include <vector>
22#if !defined(_WIN32)
23#include <unistd.h>
24#endif
25
26namespace eve::graphics::vulkan {
27namespace {
28
29
30constexpr auto kHostVisibleCoherent = vk::MemoryPropertyFlagBits::eHostVisible |
31 vk::MemoryPropertyFlagBits::eHostCoherent;
32
33template <typename T, size_t N>
34std::vector<T> embeddedSpirv(const T (&words)[N]) {
35 return {words, words + N};
36}
37
38template <typename Slots>
39auto &currentSlot(Slots &slots, size_t slotCount, size_t slotIndex) {
40 if (slots.size() != slotCount) slots.resize(slotCount);
41 return slots[slotIndex];
42}
43
44template <typename FrameBuffers>
45void releaseFrame2dBuffers(FrameBuffers &buffers) {
46 for (auto &buffer : buffers.solidBufs) buffer.release();
47 buffers.solidBufs.clear();
48 for (auto &buffer : buffers.texBufs) buffer.release();
49 buffers.texBufs.clear();
50}
51
52void setViewportAndScissor(vk::CommandBuffer cb, uint32_t width, uint32_t height) {
53 const vk::Viewport viewport{0.f, 0.f, float(width), float(height), 0.f, 1.f};
54 const vk::Rect2D scissor{{0, 0}, {width, height}};
55 cb.setViewport(0, 1, &viewport);
56 cb.setScissor(0, 1, &scissor);
57}
58
59vk::PipelineLayout createPipelineLayout(vkb::Device &device,
60 vk::DescriptorSetLayout setLayout = {},
61 const vk::PushConstantRange *pushConstant = nullptr) {
62 vk::PipelineLayoutCreateInfo info{};
63 if (setLayout) {
64 info.setLayoutCount = 1;
65 info.pSetLayouts = &setLayout;
66 }
67 if (pushConstant) {
68 info.pushConstantRangeCount = 1;
69 info.pPushConstantRanges = pushConstant;
70 }
71 return device->createPipelineLayout(info);
72}
73
74vk::PushConstantRange pushConstantRange(vk::ShaderStageFlags stages, uint32_t size) {
75 vk::PushConstantRange range{};
76 range.stageFlags = stages;
77 range.size = size;
78 return range;
79}
80
81void destroySampler(vkb::Device &device, vk::Sampler &sampler) {
82 if (!sampler) return;
83 device->destroySampler(sampler);
84 sampler = vk::Sampler{};
85}
86
87void destroyPipeline(vkb::Device &device, vk::Pipeline &pipeline) {
88 if (!pipeline) return;
89 device->destroyPipeline(pipeline);
90 pipeline = vk::Pipeline{};
91}
92
93void destroyPipelineLayout(vkb::Device &device, vk::PipelineLayout &layout) {
94 if (!layout) return;
95 device->destroyPipelineLayout(layout);
96 layout = vk::PipelineLayout{};
97}
98
101vkb::HostVertexBuffer &meshDrawVertices(GpuMesh &mesh) {
102 if (!mesh.dynamic) return mesh.vertices;
103 const size_t slot = size_t((mesh.dynamicWriteCount - 1) % GpuMesh::kDynamicVertexCopies);
104 return mesh.dynVertices[slot];
105}
106
107vkb::GenericBuffer &meshDrawIndices(GpuMesh &mesh) {
108 if (!mesh.dynamic) return mesh.indices;
109 const size_t slot = size_t((mesh.dynamicWriteCount - 1) % GpuMesh::kDynamicVertexCopies);
110 return mesh.dynIndices[slot];
111}
112
113void drawIndexedMesh(vk::CommandBuffer cb, GpuMesh &mesh) {
114 const vk::DeviceSize offset = 0;
115 cb.bindVertexBuffers(0, 1, meshDrawVertices(mesh), &offset);
116 cb.bindIndexBuffer(meshDrawIndices(mesh).buffer, 0, mesh.indexType);
117 cb.drawIndexed(mesh.indexCount, 1, 0, 0, 0);
118}
119
123void ensureDynamicRing(GpuMesh &gpu) {
124 if (gpu.dynamic) return;
125 gpu.dynamic = true;
126 gpu.dynamicWriteCount = 0;
127 gpu.cpuIndices.resize(gpu.indexCount);
128 if (gpu.indexCount > 0 && gpu.indices.buffer) {
129 void *ptr = gpu.indices.map();
130 if (gpu.indexType == vk::IndexType::eUint16) {
131 const auto *src = static_cast<const uint16_t *>(ptr);
132 for (uint32_t i = 0; i < gpu.indexCount; ++i)
133 gpu.cpuIndices[size_t(i)] = src[i];
134 } else {
135 std::memcpy(gpu.cpuIndices.data(), ptr,
136 size_t(gpu.indexCount) * sizeof(uint32_t));
137 }
138 gpu.indices.unmap();
139 }
140 gpu.indexType = vk::IndexType::eUint32;
141}
142
145void writeDynamicMesh(GpuMesh &gpu, const std::vector<MeshVertex> &verts, vkb::Device &device,
146 vkb::FrameSlot frame, const uint32_t *indices, int indexCount) {
147 const size_t slot = size_t(gpu.dynamicWriteCount % GpuMesh::kDynamicVertexCopies);
148 gpu.dynVertices[slot].allocate<MeshVertex>(frame, device, verts);
149 if (indices && indexCount > 0) {
150 gpu.cpuIndices.assign(indices, indices + indexCount);
151 gpu.indexCount = uint32_t(indexCount);
152 }
153 if (!gpu.cpuIndices.empty()) {
154 auto &ib = gpu.dynIndices[slot];
155 ib.allocate(frame, device, vk::BufferUsageFlagBits::eIndexBuffer,
156 vk::DeviceSize(gpu.cpuIndices.size()) * sizeof(uint32_t),
157 kHostVisibleCoherent);
158 ib.updateLocal(frame, gpu.cpuIndices.data(),
159 vk::DeviceSize(gpu.cpuIndices.size()) * sizeof(uint32_t));
160 }
161 ++gpu.dynamicWriteCount;
162}
163
164std::unique_ptr<GpuMesh> uploadGpuMesh(vkb::Device &device, vkb::FrameSlot frame,
165 const std::vector<MeshVertex> &vertices,
166 const std::vector<uint32_t> &indices) {
167 auto gpu = std::make_unique<GpuMesh>();
168 gpu->vertices.allocate<MeshVertex>(frame, device, vertices);
169 gpu->indices.allocate(frame, device, vk::BufferUsageFlagBits::eIndexBuffer,
170 indices.size() * sizeof(uint32_t), kHostVisibleCoherent);
171 gpu->indices.updateLocal(frame, indices.data(), indices.size() * sizeof(uint32_t));
172 gpu->indexCount = uint32_t(indices.size());
173 return gpu;
174}
175
177std::unique_ptr<GpuMesh> uploadGpuMesh16(vkb::Device &device, vkb::FrameSlot frame,
178 const std::vector<MeshVertex> &vertices,
179 const std::vector<uint16_t> &indices) {
180 auto gpu = std::make_unique<GpuMesh>();
181 gpu->vertices.allocate<MeshVertex>(frame, device, vertices);
182 gpu->indices.allocate(frame, device, vk::BufferUsageFlagBits::eIndexBuffer,
183 indices.size() * sizeof(uint16_t), kHostVisibleCoherent);
184 gpu->indices.updateLocal(frame, indices.data(), indices.size() * sizeof(uint16_t));
185 gpu->indexCount = uint32_t(indices.size());
186 gpu->indexType = vk::IndexType::eUint16;
187 return gpu;
188}
189
190std::unique_ptr<Mesh> makeMeshHandle(GpuMesh &gpu) {
191 auto mesh = std::make_unique<Mesh>();
192 mesh->indexCount = int(gpu.indexCount);
193 mesh->gpuHandle = &gpu;
194 return mesh;
195}
196
197void assignMeshBounds(Mesh *mesh, const std::vector<MeshVertex> &verts) {
198 if (!mesh || verts.empty()) return;
199 glm::vec3 c(0.f);
200 for (const auto &v : verts) c += v.pos;
201 c /= float(verts.size());
202 mesh->boundsCx = c.x;
203 mesh->boundsCy = c.y;
204 mesh->boundsCz = c.z;
205 float r = 0.f;
206 for (const auto &v : verts) {
207 const glm::vec3 d = v.pos - c;
208 const float len = std::sqrt(d.x * d.x + d.y * d.y + d.z * d.z);
209 if (len > r) r = len;
210 }
211 // Same degenerate-mesh rule as Mesh::computeBounds: keep a tiny non-zero
212 // sphere so hasBounds() stays meaningful for culling.
213 mesh->boundsRadius = r > 0.f ? r : 1e-4f;
214}
215
217void updateRingLocal(vkb::GenericBuffer &ring, vk::DeviceSize byteOffset, const void *data,
218 vk::DeviceSize bytes) {
219 if (!ring.buffer || !data || bytes == 0) return;
220 void *ptr = ring.map();
221 std::memcpy(static_cast<char *>(ptr) + byteOffset, data, size_t(bytes));
222 ring.unmap();
223}
224
225template <typename T>
226inline T alignUpValue(T value, T align) {
227 return align > 0 ? (value + align - 1) / align * align : value;
228}
229
230vk::PipelineColorBlendAttachmentState makeBlendAttachment(BlendMode mode) {
231 vk::PipelineColorBlendAttachmentState att{};
232 att.colorWriteMask =
233 vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
234 vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
235 att.blendEnable = true;
236 if (mode == BlendMode::Additive) {
237 att.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
238 att.dstColorBlendFactor = vk::BlendFactor::eOne;
239 att.colorBlendOp = vk::BlendOp::eAdd;
240 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
241 att.dstAlphaBlendFactor = vk::BlendFactor::eOne;
242 att.alphaBlendOp = vk::BlendOp::eAdd;
243 } else {
244 att.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
245 att.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
246 att.colorBlendOp = vk::BlendOp::eAdd;
247 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
248 att.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
249 att.alphaBlendOp = vk::BlendOp::eAdd;
250 }
251 return att;
252}
253
254class ShaderModulePair {
255public:
256 ShaderModulePair(vkb::Device &device, const std::vector<uint32_t> &vert,
257 const std::vector<uint32_t> &frag)
258 : device(device),
259 vert(vkb::PipelineBuilder::createShaderModule(device.instance, vert)),
260 frag(vkb::PipelineBuilder::createShaderModule(device.instance, frag)) {}
261
262 ~ShaderModulePair() {
263 device->destroyShaderModule(vert);
264 device->destroyShaderModule(frag);
265 }
266
267 ShaderModulePair(const ShaderModulePair &) = delete;
268 ShaderModulePair &operator=(const ShaderModulePair &) = delete;
269
270 vkb::Device &device;
271 vk::ShaderModule vert;
272 vk::ShaderModule frag;
273};
274
275
276
277vk::Format pickGBufferColorFormat(vkb::Device &device) {
278 (void)device;
279 return vk::Format::eR8G8B8A8Unorm;
280}
281
282vk::SampleCountFlagBits sampleCountFlagFor(int samples) {
283 if (samples >= 8) return vk::SampleCountFlagBits::e8;
284 if (samples >= 4) return vk::SampleCountFlagBits::e4;
285 if (samples >= 2) return vk::SampleCountFlagBits::e2;
286 return vk::SampleCountFlagBits::e1;
287}
288
289
290
291uint32_t rgba8MipBytes(uint32_t width, uint32_t height) {
292 // Matches VKBuilder GenericImage::upload packing for eR8G8B8A8Unorm.
293 return 4u * width * height;
294}
295
296void appendBoxFilteredMip(std::vector<uint8_t> &out, const uint8_t *src, uint32_t srcW,
297 uint32_t srcH, uint32_t dstW, uint32_t dstH) {
298 const size_t base = out.size();
299 out.resize(base + size_t(rgba8MipBytes(dstW, dstH)));
300 uint8_t *dst = out.data() + base;
301 for (uint32_t y = 0; y < dstH; ++y) {
302 const uint32_t y0 = std::min(y * 2u, srcH - 1u);
303 const uint32_t y1 = std::min(y0 + 1u, srcH - 1u);
304 for (uint32_t x = 0; x < dstW; ++x) {
305 const uint32_t x0 = std::min(x * 2u, srcW - 1u);
306 const uint32_t x1 = std::min(x0 + 1u, srcW - 1u);
307 uint32_t acc[4] = {0, 0, 0, 0};
308 const uint32_t samples[4][2] = {{x0, y0}, {x1, y0}, {x0, y1}, {x1, y1}};
309 for (const auto &s : samples) {
310 const size_t i = (size_t(s[1]) * srcW + s[0]) * 4u;
311 acc[0] += src[i + 0];
312 acc[1] += src[i + 1];
313 acc[2] += src[i + 2];
314 acc[3] += src[i + 3];
315 }
316 const size_t o = (size_t(y) * dstW + x) * 4u;
317 dst[o + 0] = static_cast<uint8_t>((acc[0] + 2u) / 4u);
318 dst[o + 1] = static_cast<uint8_t>((acc[1] + 2u) / 4u);
319 dst[o + 2] = static_cast<uint8_t>((acc[2] + 2u) / 4u);
320 dst[o + 3] = static_cast<uint8_t>((acc[3] + 2u) / 4u);
321 }
322 }
323}
324
326std::vector<uint8_t> buildMipChain2D(const uint8_t *rgba, uint32_t width, uint32_t height,
327 uint32_t mipLevels) {
328 std::vector<uint8_t> packed;
329 packed.reserve(size_t(width) * size_t(height) * 4u * 2u);
330 packed.insert(packed.end(), rgba, rgba + size_t(rgba8MipBytes(width, height)));
331
332 uint32_t srcW = width;
333 uint32_t srcH = height;
334 size_t srcOffset = 0;
335 for (uint32_t level = 1; level < mipLevels; ++level) {
336 const uint32_t dstW = std::max(srcW >> 1, 1u);
337 const uint32_t dstH = std::max(srcH >> 1, 1u);
338 const uint8_t *src = packed.data() + srcOffset;
339 srcOffset = packed.size();
340 appendBoxFilteredMip(packed, src, srcW, srcH, dstW, dstH);
341 srcW = dstW;
342 srcH = dstH;
343 }
344 return packed;
345}
346
351std::vector<uint8_t> buildMipChainCube(const uint8_t *rgbaFaces, uint32_t faceSize,
352 uint32_t mipLevels) {
353 const uint32_t faceBytes = rgba8MipBytes(faceSize, faceSize);
354 std::vector<std::vector<uint8_t>> faceChains(6);
355 for (uint32_t f = 0; f < 6; ++f) {
356 faceChains[f] = buildMipChain2D(rgbaFaces + size_t(f) * faceBytes, faceSize, faceSize,
357 mipLevels);
358 }
359
360 std::vector<uint8_t> packed;
361 uint32_t w = faceSize;
362 uint32_t h = faceSize;
363 size_t faceOffsets[6] = {0, 0, 0, 0, 0, 0};
364 for (uint32_t level = 0; level < mipLevels; ++level) {
365 const uint32_t levelBytes = rgba8MipBytes(w, h);
366 for (uint32_t f = 0; f < 6; ++f) {
367 const uint8_t *src = faceChains[f].data() + faceOffsets[f];
368 packed.insert(packed.end(), src, src + levelBytes);
369 faceOffsets[f] += levelBytes;
370 }
371 w = std::max(w >> 1, 1u);
372 h = std::max(h >> 1, 1u);
373 }
374 return packed;
375}
376
377TextureCreateInfo normalizeTextureInfo(TextureCreateInfo info) {
378 if (info.generateMipmaps && info.sampler.mipmap == MipmapMode::Disabled)
379 info.sampler.mipmap = MipmapMode::Linear;
380 if (info.sampler.maxAnisotropy < 1.f) info.sampler.maxAnisotropy = 1.f;
381 return info;
382}
383
384
385
386std::string normalizeTexPath(std::string path) {
387 for (char &c : path) {
388 if (c == '\\') c = '/';
389 }
390 while (path.size() >= 2 && path[0] == '.' && path[1] == '/') path.erase(0, 2);
391 while (path.size() > 1 && path.back() == '/') path.pop_back();
392 return path;
393}
394
395
396
397std::vector<uint32_t> loadSpirvBytes(const void *data, size_t size) {
398 if (!data || size < 4 || (size % 4) != 0)
399 throw Exception("SPIR-V: invalid size %zu", size);
400 const auto *words = static_cast<const uint32_t *>(data);
401 if (words[0] != 0x07230203)
402 throw Exception("SPIR-V: bad magic (expected 0x07230203)");
403 return std::vector<uint32_t>(words, words + size / 4);
404}
405
406std::vector<uint32_t> readSpirvFile(const std::string &path) {
407 auto *fs = filesystem::Filesystem::create();
408 std::unique_ptr<filesystem::FileData> fd(fs->read(path));
409 if (!fd) throw Exception("newShaderFromSpvFile: failed to read '%s'", path.c_str());
410 return loadSpirvBytes(fd->getData(), fd->getSize());
411}
412
413std::vector<uint32_t> compileGlslWithGlslc(const std::string &source, const char *stage) {
414 if (source.empty()) throw Exception("newShader: empty %s GLSL", stage);
415#if defined(_WIN32)
416 (void)source;
417 (void)stage;
418 throw Exception("newShader: GLSL compile via glslc is not supported on Windows; "
419 "use newShaderFromSpv / newShaderFromSpvFile");
420#else
421 char inPath[] = "/tmp/eve_shader_XXXXXX";
422 int fd = mkstemp(inPath);
423 if (fd < 0) throw Exception("newShader: mkstemp failed");
424 std::string outPath = std::string(inPath) + ".spv";
425 {
426 ssize_t n = write(fd, source.data(), source.size());
427 close(fd);
428 if (n < 0 || size_t(n) != source.size()) {
429 unlink(inPath);
430 throw Exception("newShader: failed to write temp GLSL");
431 }
432 }
433
434 std::string cmd = std::string("glslc -fshader-stage=") + stage + " \"" + inPath + "\" -o \"" +
435 outPath + "\" 2>&1";
436 FILE *pipe = popen(cmd.c_str(), "r");
437 std::string err;
438 if (pipe) {
439 char buf[256];
440 while (fgets(buf, sizeof(buf), pipe)) err += buf;
441 int status = pclose(pipe);
442 unlink(inPath);
443 if (status != 0) {
444 unlink(outPath.c_str());
445 throw Exception("newShader: glslc failed for %s:\n%s", stage, err.c_str());
446 }
447 } else {
448 unlink(inPath);
449 throw Exception("newShader: glslc not available (popen failed)");
450 }
451
452 FILE *f = fopen(outPath.c_str(), "rb");
453 if (!f) {
454 unlink(outPath.c_str());
455 throw Exception("newShader: failed to open compiled SPIR-V");
456 }
457 fseek(f, 0, SEEK_END);
458 long sz = ftell(f);
459 fseek(f, 0, SEEK_SET);
460 std::vector<uint8_t> bytes(static_cast<size_t>(sz > 0 ? sz : 0));
461 if (sz > 0 && fread(bytes.data(), 1, static_cast<size_t>(sz), f) != static_cast<size_t>(sz)) {
462 fclose(f);
463 unlink(outPath.c_str());
464 throw Exception("newShader: failed to read compiled SPIR-V");
465 }
466 fclose(f);
467 unlink(outPath.c_str());
468 return loadSpirvBytes(bytes.data(), bytes.size());
469#endif
470}
471
472
473} // namespace
474} // namespace eve::graphics::vulkan
std::vector< std::uint32_t > verts
Definition Builder.cpp:27
std::string value
std::string layout
vkb::Device & device
vk::ShaderModule vert
vk::ShaderModule frag
int y
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
glm::vec3 n
Definition Grass.cpp:64
int h
int w
JobStatus status
void * ptr
uint32_t c
int width
float f
Mesh * mesh
Light2D::Data * data
int d
int v
uint32_t s
Definition Weather.cpp:28
BlendMode
2D quad blend mode (drawn in draw order within a layer).
Definition BlendMode.h:6
WidgetDesc viewport(std::string id, float width, float height)
Definition Widget.cpp:353
static constexpr size_t kDynamicVertexCopies
Ring copies for per-frame updated meshes (skin/morph/sprite stack). Writing the next copy never races...
Definition Graphics.h:145