/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "rendering/vulkan14/vulkan14geometry.h" #include "rendering/vulkan14/vulkan14texture.h" #include namespace vk14 { namespace { // what opengl reads for the user data of a vertex without any (the attribute isn't given) gfx::vertex_userdata const no_userdata{glm::vec4{0.f, 0.f, 0.f, 1.f}}; // the numbers the data of the banks get, unique across the banks (a bank made where another one was gets a new one) std::atomic g_generation{0}; } // namespace VkPrimitiveTopology topology_of(unsigned int const Type) { switch (Type) { case GL_POINTS: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; case GL_LINES: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; case GL_LINE_STRIP: case GL_LINE_LOOP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; case GL_TRIANGLE_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; case GL_TRIANGLE_FAN: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; case GL_TRIANGLES: default: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; } } vulkan14_geometrybank::~vulkan14_geometrybank() { release_(); } void vulkan14_geometrybank::create_(gfx::geometry_handle const &Geometry) { // a new chunk: the buffers are made anew with it m_layoutdirty = true; } void vulkan14_geometrybank::replace_(gfx::geometry_handle const &Geometry) { if (m_layoutdirty) { return; } auto const index{Geometry.chunk - 1}; if (index >= m_places.size()) { m_layoutdirty = true; return; } auto const &chunk{gfx::geometry_bank::chunk(Geometry)}; auto &place{m_places[index]}; auto const loop{chunk.type == GL_LINE_LOOP}; auto const vertexcount{static_cast(chunk.vertices.size()) + ((loop && chunk.indices.empty() && (false == chunk.vertices.empty())) ? 1u : 0u)}; auto const indexcount{static_cast(chunk.indices.size()) + ((loop && (false == chunk.indices.empty())) ? 1u : 0u)}; if ((vertexcount != place.vertexcount) || (indexcount != place.indexcount)) { // it doesn't fit in its place any more m_layoutdirty = true; return; } place.dirty = true; m_chunksdirty = true; } void vulkan14_geometrybank::release_() { auto *deletions{m_resources.deletions}; if ((deletions != nullptr) && (m_resources.uploads != nullptr) && (m_vertices || m_userdata || m_indices)) { deletions->push(m_resources.uploads->current_value(), [vertices = std::move(m_vertices), userdata = std::move(m_userdata), indices = std::move(m_indices)]() {}); } m_vertices.reset(); m_userdata.reset(); m_indices.reset(); m_places.clear(); m_bytes = 0; m_layoutdirty = true; m_chunksdirty = false; } void vulkan14_geometrybank::gpu_vertices(geometry_chunk const &Chunk, std::vector &Vertices, std::vector &Userdata) const { Vertices.insert(Vertices.end(), Chunk.vertices.begin(), Chunk.vertices.end()); if (Chunk.userdata.size() >= Chunk.vertices.size()) { Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.begin() + Chunk.vertices.size()); } else { Userdata.insert(Userdata.end(), Chunk.userdata.begin(), Chunk.userdata.end()); Userdata.resize(Vertices.size(), no_userdata); } if ((Chunk.type == GL_LINE_LOOP) && Chunk.indices.empty() && (false == Chunk.vertices.empty())) { // back to the first vertex Vertices.push_back(Chunk.vertices.front()); Userdata.push_back(Chunk.userdata.empty() ? no_userdata : Chunk.userdata.front()); } } bool vulkan14_geometrybank::build() { release_(); std::vector vertices; std::vector userdata; std::vector indices; m_places.resize(m_chunks.size()); for (std::size_t i = 0; i < m_chunks.size(); ++i) { auto const &chunk{m_chunks[i]}; auto &place{m_places[i]}; place.vertexfirst = static_cast(vertices.size()); place.indexfirst = static_cast(indices.size()); gpu_vertices(chunk, vertices, userdata); place.vertexcount = static_cast(vertices.size()) - place.vertexfirst; indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end()); if ((chunk.type == GL_LINE_LOOP) && (false == chunk.indices.empty())) { indices.push_back(chunk.indices.front()); } place.indexcount = static_cast(indices.size()) - place.indexfirst; place.dirty = false; place.generation = ++g_generation; } m_layoutdirty = false; m_chunksdirty = false; if (vertices.empty()) { return false; } auto &memory{*m_resources.memory}; auto &uploads{*m_resources.uploads}; // with the ray tracing the vertices and the indices are built into acceleration structures, and the shading reads them auto const raytracing{m_resources.raytracing ? (VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) : 0}; auto const make = [&](std::shared_ptr &Buffer, void const *Data, VkDeviceSize const Size, VkBufferUsageFlags const Usage, char const *Name) { Buffer = std::make_shared(); if ((false == Buffer->create(memory, Size, Usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT | raytracing, memory_kind::device, Name)) || (false == uploads.upload_buffer(Buffer, Data, Size))) { Buffer.reset(); return false; } m_bytes += Size; return true; }; auto ok{make(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry vertices") && make(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, "geometry user data")}; if (ok && (false == indices.empty())) { ok = make(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), VK_BUFFER_USAGE_INDEX_BUFFER_BIT, "geometry indices"); } if (false == ok) { log_error("no memory for the geometry of a bank of " + std::to_string(vertices.size()) + " vertices"); release_(); // tried again when it's drawn the next time m_layoutdirty = true; return false; } return true; } void vulkan14_geometrybank::update() { m_chunksdirty = false; if ((false == bool(m_vertices)) || (false == bool(m_userdata))) { return; } std::vector vertices; std::vector userdata; std::vector indices; std::vector> vertexparts, userdataparts, indexparts; for (std::size_t i = 0; i < m_places.size(); ++i) { auto &place{m_places[i]}; if (false == place.dirty) { continue; } place.dirty = false; place.generation = ++g_generation; auto const &chunk{m_chunks[i]}; gpu_vertices(chunk, vertices, userdata); vertexparts.emplace_back(place.vertexfirst * sizeof(gfx::basic_vertex), place.vertexcount * sizeof(gfx::basic_vertex)); userdataparts.emplace_back(place.vertexfirst * sizeof(gfx::vertex_userdata), place.vertexcount * sizeof(gfx::vertex_userdata)); if ((place.indexcount > 0) && m_indices) { indices.insert(indices.end(), chunk.indices.begin(), chunk.indices.end()); if (chunk.type == GL_LINE_LOOP) { indices.push_back(chunk.indices.front()); } indexparts.emplace_back(place.indexfirst * sizeof(gfx::basic_index), place.indexcount * sizeof(gfx::basic_index)); } } auto &uploads{*m_resources.uploads}; if (false == vertices.empty()) { uploads.upload_buffer(m_vertices, vertices.data(), vertices.size() * sizeof(gfx::basic_vertex), vertexparts); uploads.upload_buffer(m_userdata, userdata.data(), userdata.size() * sizeof(gfx::vertex_userdata), userdataparts); } if (false == indices.empty()) { uploads.upload_buffer(m_indices, indices.data(), indices.size() * sizeof(gfx::basic_index), indexparts); } } bool vulkan14_geometrybank::source(gfx::geometry_handle const &Geometry, chunk_source &Source) { if ((Geometry.chunk == 0) || (Geometry.chunk > m_chunks.size())) { return false; } if (m_layoutdirty) { build(); } else if (m_chunksdirty) { update(); } auto const index{Geometry.chunk - 1}; if ((false == bool(m_vertices)) || (index >= m_places.size())) { return false; } auto const &place{m_places[index]}; Source.vertices = m_vertices->handle(); Source.userdata = m_userdata->handle(); Source.topology = topology_of(m_chunks[index].type); Source.vertexfirst = place.vertexfirst; Source.vertexcount = place.vertexcount; Source.generation = place.generation; if (m_resources.raytracing) { Source.vertex_address = m_vertices->address(); Source.index_address = (m_indices ? m_indices->address() : 0); } if (place.indexcount > 0) { Source.indices = m_indices->handle(); Source.count = place.indexcount; Source.first = place.indexfirst; Source.basevertex = static_cast(place.vertexfirst); } else { Source.indices = VK_NULL_HANDLE; Source.count = place.vertexcount; Source.first = place.vertexfirst; Source.basevertex = 0; } return Source.count > 0; } bool vulkan14_geometrybank::indirect_source_(gfx::geometry_handle const &Geometry, gfx::indirect_source &Source) { m_last = chunk_source{}; if (false == source(Geometry, m_last)) { return false; } Source.count = m_last.count; Source.first = m_last.first; Source.basevertex = m_last.basevertex; Source.indexed = (m_last.indices != VK_NULL_HANDLE); Source.type = m_chunks[Geometry.chunk - 1].type; return true; } } // namespace vk14