30constexpr auto kHostVisibleCoherent = vk::MemoryPropertyFlagBits::eHostVisible |
31 vk::MemoryPropertyFlagBits::eHostCoherent;
33template <
typename T,
size_t N>
35std::vector<T> embeddedSpirv(
const T (&words)[N]) {
36 return {words, words + N};
39template <
typename Slots>
41auto ¤tSlot(Slots &
slots,
size_t slotCount,
size_t slotIndex) {
42 if (
slots.size() != slotCount)
slots.resize(slotCount);
43 return slots[slotIndex];
46template <
typename FrameBuffers>
48void releaseFrame2dBuffers(FrameBuffers &buffers) {
50 buffers.solidBufs.clear();
52 buffers.texBufs.clear();
56void setViewportAndScissor(vk::CommandBuffer cb, uint32_t
width, uint32_t
height) {
59 cb.setViewport(0, 1, &viewport);
60 cb.setScissor(0, 1, &scissor);
64vk::PipelineLayout createPipelineLayout(vkb::Device &
device,
65 vk::DescriptorSetLayout setLayout = {},
67 vk::PipelineLayoutCreateInfo
info{};
69 info.setLayoutCount = 1;
70 info.pSetLayouts = &setLayout;
73 info.pushConstantRangeCount = 1;
80vk::PushConstantRange pushConstantRange(vk::ShaderStageFlags stages, uint32_t
size) {
81 vk::PushConstantRange
range{};
82 range.stageFlags = stages;
88void destroySampler(vkb::Device &
device, vk::Sampler &
sampler) {
95void destroyPipeline(vkb::Device &
device, vk::Pipeline &pipeline) {
96 if (!pipeline)
return;
97 device->destroyPipeline(pipeline);
98 pipeline = vk::Pipeline{};
102void destroyPipelineLayout(vkb::Device &
device, vk::PipelineLayout &
layout) {
105 layout = vk::PipelineLayout{};
111vkb::HostVertexBuffer &meshDrawVertices(GpuMesh &
mesh) {
112 if (!
mesh.dynamic)
return mesh.vertices;
114 return mesh.dynVertices[slot];
118vkb::GenericBuffer &meshDrawIndices(GpuMesh &
mesh) {
119 if (!
mesh.dynamic)
return mesh.indices;
121 return mesh.dynIndices[slot];
125void drawIndexedMesh(vk::CommandBuffer cb, GpuMesh &
mesh, uint32_t
count = 1, uint32_t
first = 0) {
126 const vk::DeviceSize
offset = 0;
127 cb.bindVertexBuffers(0, 1, meshDrawVertices(
mesh), &
offset);
128 cb.bindIndexBuffer(meshDrawIndices(
mesh).
buffer, 0,
mesh.indexType);
136void ensureDynamicRing(GpuMesh &gpu) {
137 if (gpu.dynamic)
return;
139 gpu.dynamicWriteCount = 0;
140 gpu.cpuIndices.resize(gpu.indexCount);
141 if (gpu.indexCount > 0 && gpu.indices.buffer) {
142 void *
ptr = gpu.indices.map();
143 if (gpu.indexType == vk::IndexType::eUint16) {
144 const auto *src =
static_cast<const uint16_t *
>(
ptr);
145 for (uint32_t i = 0; i < gpu.indexCount; ++i)
146 gpu.cpuIndices[
size_t(i)] = src[i];
149 std::memcpy(gpu.cpuIndices.data(),
ptr,
151 size_t(gpu.indexCount) *
sizeof(uint32_t));
155 gpu.indexType = vk::IndexType::eUint32;
161void writeDynamicMesh(GpuMesh &gpu,
const std::vector<MeshVertex> &
verts, vkb::Device &
device,
164 gpu.vertexCount = uint32_t(
verts.size());
165 gpu.dynVertices[slot].allocate<MeshVertex>(frame,
device,
verts);
170 if (!gpu.cpuIndices.empty()) {
171 auto &ib = gpu.dynIndices[slot];
172 ib.allocate(frame,
device, vk::BufferUsageFlagBits::eIndexBuffer,
174 vk::DeviceSize(gpu.cpuIndices.size()) *
sizeof(uint32_t),
175 kHostVisibleCoherent);
176 ib.updateLocal(frame, gpu.cpuIndices.data(),
178 vk::DeviceSize(gpu.cpuIndices.size()) *
sizeof(uint32_t));
180 ++gpu.dynamicWriteCount;
184std::unique_ptr<GpuMesh> uploadGpuMesh(vkb::Device &
device, vkb::FrameSlot frame,
185 const std::vector<MeshVertex> &
vertices,
186 const std::vector<uint32_t> &
indices) {
187 auto gpu = std::make_unique<GpuMesh>();
188 gpu->vertexCount = uint32_t(
vertices.size());
190 gpu->indices.allocate(frame,
device, vk::BufferUsageFlagBits::eIndexBuffer,
191 indices.size() *
sizeof(uint32_t), kHostVisibleCoherent);
192 gpu->indices.updateLocal(frame,
indices.data(),
indices.size() *
sizeof(uint32_t));
193 gpu->indexCount = uint32_t(
indices.size());
199std::unique_ptr<GpuMesh> uploadGpuMesh16(vkb::Device &
device, vkb::FrameSlot frame,
200 const std::vector<MeshVertex> &
vertices,
201 const std::vector<uint16_t> &
indices) {
202 auto gpu = std::make_unique<GpuMesh>();
203 gpu->vertexCount = uint32_t(
vertices.size());
205 gpu->indices.allocate(frame,
device, vk::BufferUsageFlagBits::eIndexBuffer,
206 indices.size() *
sizeof(uint16_t), kHostVisibleCoherent);
207 gpu->indices.updateLocal(frame,
indices.data(),
indices.size() *
sizeof(uint16_t));
208 gpu->indexCount = uint32_t(
indices.size());
209 gpu->indexType = vk::IndexType::eUint16;
214std::unique_ptr<Mesh> makeMeshHandle(GpuMesh &gpu) {
215 auto mesh = std::make_unique<Mesh>();
216 mesh->indexCount = int(gpu.indexCount);
217 mesh->gpuVertexCount = int(gpu.vertexCount);
218 mesh->gpuHandle = &gpu;
223void assignMeshBounds(Mesh *
mesh,
const std::vector<MeshVertex> &
verts) {
229 mesh->boundsCx =
c.x;
230 mesh->boundsCy =
c.y;
231 mesh->boundsCz =
c.z;
233 for (
const auto &
v :
verts) {
234 const glm::vec3
d =
v.pos -
c;
235 const float len = std::sqrt(
d.x *
d.x +
d.y *
d.y +
d.z *
d.z);
236 if (len >
r)
r = len;
240 mesh->boundsRadius =
r > 0.f ?
r : 1e-4f;
245void updateRingLocal(vkb::GenericBuffer &ring, vk::DeviceSize byteOffset,
const void *data,
246 vk::DeviceSize
bytes) {
247 if (!ring.buffer || !data ||
bytes == 0)
return;
248 void *
ptr = ring.map();
250 std::memcpy(
static_cast<char *
>(
ptr) + byteOffset, data,
size_t(
bytes));
256inline T alignUpValue(T
value, T align) {
257 return align > 0 ? (
value + align - 1) / align * align :
value;
261vk::PipelineColorBlendAttachmentState makeBlendAttachment(
BlendMode mode) {
262 vk::PipelineColorBlendAttachmentState att{};
264 vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
265 vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
266 att.blendEnable =
true;
268 att.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
269 att.dstColorBlendFactor = vk::BlendFactor::eOne;
270 att.colorBlendOp = vk::BlendOp::eAdd;
271 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
272 att.dstAlphaBlendFactor = vk::BlendFactor::eOne;
273 att.alphaBlendOp = vk::BlendOp::eAdd;
275 att.srcColorBlendFactor = vk::BlendFactor::eOne;
276 att.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
277 att.colorBlendOp = vk::BlendOp::eAdd;
278 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
279 att.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
280 att.alphaBlendOp = vk::BlendOp::eAdd;
282 att.srcColorBlendFactor = vk::BlendFactor::eDstColor;
283 att.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
284 att.colorBlendOp = vk::BlendOp::eAdd;
285 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
286 att.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
287 att.alphaBlendOp = vk::BlendOp::eAdd;
289 att.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
290 att.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
291 att.colorBlendOp = vk::BlendOp::eAdd;
292 att.srcAlphaBlendFactor = vk::BlendFactor::eOne;
293 att.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
294 att.alphaBlendOp = vk::BlendOp::eAdd;
300class ShaderModulePair {
303 ShaderModulePair(vkb::Device &device,
const std::vector<uint32_t> &vert,
304 const std::vector<uint32_t> &frag)
308 vert(vkb::PipelineBuilder::createShaderModule(
device.instance,
vert)),
310 frag(vkb::PipelineBuilder::createShaderModule(
device.instance,
frag)) {}
313 ~ShaderModulePair() {
314 device->destroyShaderModule(vert);
315 device->destroyShaderModule(frag);
318 ShaderModulePair(
const ShaderModulePair &) =
delete;
319 ShaderModulePair &operator=(
const ShaderModulePair &) =
delete;
329vk::Format pickGBufferColorFormat(vkb::Device &
device) {
331 return vk::Format::eR8G8B8A8Unorm;
335vk::SampleCountFlagBits sampleCountFlagFor(
int samples) {
336 if (samples >= 8)
return vk::SampleCountFlagBits::e8;
337 if (samples >= 4)
return vk::SampleCountFlagBits::e4;
338 if (samples >= 2)
return vk::SampleCountFlagBits::e2;
339 return vk::SampleCountFlagBits::e1;
345uint32_t rgba8MipBytes(uint32_t
width, uint32_t
height) {
351void appendBoxFilteredMip(std::vector<uint8_t> &out,
const uint8_t *src, uint32_t srcW,
352 uint32_t srcH, uint32_t dstW, uint32_t dstH) {
353 const size_t base = out.size();
354 out.resize(base +
size_t(rgba8MipBytes(dstW, dstH)));
355 uint8_t *dst = out.data() + base;
356 for (uint32_t
y = 0;
y < dstH; ++
y) {
357 const uint32_t y0 = std::min(
y * 2u, srcH - 1u);
358 const uint32_t y1 = std::min(y0 + 1u, srcH - 1u);
359 for (uint32_t
x = 0;
x < dstW; ++
x) {
360 const uint32_t x0 = std::min(
x * 2u, srcW - 1u);
361 const uint32_t x1 = std::min(x0 + 1u, srcW - 1u);
362 uint32_t acc[4] = {0, 0, 0, 0};
363 const uint32_t samples[4][2] = {{x0, y0}, {x1, y0}, {x0, y1}, {x1, y1}};
364 for (
const auto &
s : samples) {
365 const size_t i = (size_t(
s[1]) * srcW +
s[0]) * 4u;
366 acc[0] += src[i + 0];
367 acc[1] += src[i + 1];
368 acc[2] += src[i + 2];
369 acc[3] += src[i + 3];
371 const size_t o = (size_t(
y) * dstW +
x) * 4u;
372 dst[o + 0] =
static_cast<uint8_t
>((acc[0] + 2u) / 4u);
373 dst[o + 1] =
static_cast<uint8_t
>((acc[1] + 2u) / 4u);
374 dst[o + 2] =
static_cast<uint8_t
>((acc[2] + 2u) / 4u);
375 dst[o + 3] =
static_cast<uint8_t
>((acc[3] + 2u) / 4u);
382std::vector<uint8_t> buildMipChain2D(
const uint8_t *rgba, uint32_t
width, uint32_t
height,
383 uint32_t mipLevels) {
384 std::vector<uint8_t> packed;
385 packed.reserve(
size_t(
width) *
size_t(
height) * 4u * 2u);
386 packed.insert(packed.end(), rgba, rgba +
size_t(rgba8MipBytes(
width,
height)));
388 uint32_t srcW =
width;
390 size_t srcOffset = 0;
392 const uint32_t dstW = std::max(srcW >> 1, 1u);
393 const uint32_t dstH = std::max(srcH >> 1, 1u);
394 const uint8_t *src = packed.data() + srcOffset;
395 srcOffset = packed.size();
397 appendBoxFilteredMip(packed, src, srcW, srcH, dstW, dstH);
409std::vector<uint8_t> buildMipChainCube(
const uint8_t *rgbaFaces, uint32_t faceSize,
410 uint32_t mipLevels) {
416TextureCreateInfo normalizeTextureInfo(TextureCreateInfo
info) {
419 if (
info.sampler.maxAnisotropy < 1.f)
info.sampler.maxAnisotropy = 1.f;
426std::string normalizeTexPath(std::string
path) {
427 for (
char &
c :
path) {
428 if (
c ==
'\\')
c =
'/';
430 while (
path.size() >= 2 &&
path[0] ==
'.' &&
path[1] ==
'/')
path.erase(0, 2);
431 while (
path.size() > 1 &&
path.back() ==
'/')
path.pop_back();
std::vector< std::uint32_t > verts
vk::UniqueSampler sampler
std::vector< std::uint32_t > indices
std::unique_ptr< gpgpu::GpuBuffer > buffer
std::vector< Point > vertices
std::function< eve::Result< void >()> release
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
std::vector< uint8_t > buildGgxCubemapMipChain(const uint8_t *rgbaFaces, uint32_t faceSize, uint32_t mipLevels, uint32_t sampleCount=32u)
Build a mip-major RGBA8 IBL chain: GGX specular mips plus final diffuse irradiance.
eve::BlendMode BlendMode
Compatibility alias for the shared 2D blend mode.
std::string pushConstant()
Pushes constant.
WidgetDesc viewport(std::string id, float width, float height)
Viewport.
static constexpr size_t kDynamicVertexCopies
Ring copies for per-frame updated meshes (skin/morph/sprite stack). Writing the next copy never races...