/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* texture.cpp :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: maldavid +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2023/03/31 18:03:35 by maldavid #+# #+# */ /* Updated: 2023/12/31 00:49:16 by maldavid ### ########.fr */ /* */ /* ************************************************************************** */ #include #include #include #include #include #define STB_IMAGE_IMPLEMENTATION #include #include #ifdef IMAGE_OPTIMIZED #define TILING VK_IMAGE_TILING_OPTIMAL #else #define TILING VK_IMAGE_TILING_LINEAR #endif namespace mlx { void Texture::create(uint8_t* pixels, uint32_t width, uint32_t height, VkFormat format, const char* name, bool dedicated_memory) { Image::create(width, height, format, TILING, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, name, dedicated_memory); Image::createImageView(VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_ASPECT_COLOR_BIT); Image::createSampler(); transitionLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); std::vector vertexData = { {{0, 0}, {1.f, 1.f, 1.f, 1.f}, {0.0f, 0.0f}}, {{width, 0}, {1.f, 1.f, 1.f, 1.f}, {1.0f, 0.0f}}, {{width, height}, {1.f, 1.f, 1.f, 1.f}, {1.0f, 1.0f}}, {{0, height}, {1.f, 1.f, 1.f, 1.f}, {0.0f, 1.0f}} }; std::vector indexData = { 0, 1, 2, 2, 3, 0 }; #ifdef DEBUG _vbo.create(sizeof(Vertex) * vertexData.size(), vertexData.data(), name); _ibo.create(sizeof(uint16_t) * indexData.size(), indexData.data(), name); _name = name; #else _vbo.create(sizeof(Vertex) * vertexData.size(), vertexData.data(), nullptr); _ibo.create(sizeof(uint16_t) * indexData.size(), indexData.data(), nullptr); #endif Buffer staging_buffer; std::size_t size = width * height * formatSize(format); if(pixels != nullptr) { #ifdef DEBUG staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, name, pixels); #else staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, pixels); #endif } else { std::vector default_pixels(width * height, 0x00000000); #ifdef DEBUG staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, name, default_pixels.data()); #else staging_buffer.create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, nullptr, default_pixels.data()); #endif } Image::copyFromBuffer(staging_buffer); staging_buffer.destroy(); } void Texture::setPixel(int x, int y, uint32_t color) noexcept { if(x < 0 || y < 0 || static_cast(x) > getWidth() || static_cast(y) > getHeight()) return; if(_map == nullptr) openCPUmap(); _cpu_map[(y * getWidth()) + x] = color; _has_been_modified = true; } int Texture::getPixel(int x, int y) noexcept { if(x < 0 || y < 0 || static_cast(x) > getWidth() || static_cast(y) > getHeight()) return 0; if(_map == nullptr) openCPUmap(); uint32_t color = _cpu_map[(y * getWidth()) + x]; return (color); } void Texture::openCPUmap() { if(_map != nullptr) return; #ifdef DEBUG core::error::report(e_kind::message, "Texture : enabling CPU mapping"); #endif std::size_t size = getWidth() * getHeight() * formatSize(getFormat()); _buf_map.emplace(); #ifdef DEBUG _buf_map->create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, _name.c_str()); #else _buf_map->create(Buffer::kind::dynamic, size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, nullptr); #endif Image::copyToBuffer(*_buf_map); _buf_map->mapMem(&_map); _cpu_map = std::vector(getWidth() * getHeight(), 0); std::memcpy(_cpu_map.data(), _map, size); #ifdef DEBUG core::error::report(e_kind::message, "Texture : mapped CPU memory using staging buffer"); #endif } void Texture::render(Renderer& renderer, int x, int y) { if(_has_been_modified) { std::memcpy(_map, _cpu_map.data(), _cpu_map.size() * formatSize(getFormat())); Image::copyFromBuffer(*_buf_map); _has_been_modified = false; } auto cmd = renderer.getActiveCmdBuffer(); _vbo.bind(renderer); _ibo.bind(renderer); glm::vec2 translate(x, y); vkCmdPushConstants(cmd.get(), renderer.getPipeline().getPipelineLayout(), VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(translate), &translate); vkCmdDrawIndexed(cmd.get(), static_cast(_ibo.getSize() / sizeof(uint16_t)), 1, 0, 0, 0); } void Texture::destroy() noexcept { Image::destroy(); if(_buf_map.has_value()) _buf_map->destroy(); _vbo.destroy(); _ibo.destroy(); } Texture stbTextureLoad(std::filesystem::path file, int* w, int* h) { Texture texture; int channels; uint8_t* data = nullptr; std::string filename = file.string(); if(!std::filesystem::exists(std::move(file))) core::error::report(e_kind::fatal_error, "Image : file not found '%s'", filename.c_str()); if(stbi_is_hdr(filename.c_str())) core::error::report(e_kind::fatal_error, "Texture : unsupported image format '%s'", filename.c_str()); int dummy_w; int dummy_h; data = stbi_load(filename.c_str(), (w == nullptr ? &dummy_w : w), (h == nullptr ? &dummy_h : h), &channels, 4); #ifdef DEBUG texture.create(data, (w == nullptr ? dummy_w : *w), (h == nullptr ? dummy_h : *h), VK_FORMAT_R8G8B8A8_UNORM, filename.c_str()); #else texture.create(data, (w == nullptr ? dummy_w : *w), (h == nullptr ? dummy_h : *h), VK_FORMAT_R8G8B8A8_UNORM, nullptr); #endif stbi_image_free(data); return texture; } }