diff --git a/include/reactive/Graphics/Image.hpp b/include/reactive/Graphics/Image.hpp index 65810ce..2593aa8 100644 --- a/include/reactive/Graphics/Image.hpp +++ b/include/reactive/Graphics/Image.hpp @@ -5,6 +5,16 @@ namespace rv { class Buffer; +struct ImageViewCreateInfo { + vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor; +}; + +struct SamplerCreateInfo { + vk::Filter filter = vk::Filter::eLinear; + vk::SamplerAddressMode addressMode = vk::SamplerAddressMode::eRepeat; + vk::SamplerMipmapMode mipmapMode = vk::SamplerMipmapMode::eLinear; +}; + // NOTE: // Cubemap はファイルから読み込むものと想定して // アプリ側で作成するのは 2D or 3D のみとする @@ -14,10 +24,17 @@ struct ImageCreateInfo { vk::Extent3D extent = {1, 1, 1}; + vk::ImageType imageType = vk::ImageType::e2D; + vk::Format format; uint32_t mipLevels = 1; + std::optional viewInfo; + + std::optional samplerInfo; + + // Debug std::string debugName{}; }; @@ -53,8 +70,39 @@ class Image { ~Image(); - void createImageView(vk::ImageViewType _viewType = vk::ImageViewType::e2D, - vk::ImageAspectFlags _aspect = vk::ImageAspectFlagBits::eColor) { + auto getImage() const -> vk::Image { return image; } + auto getView() const -> vk::ImageView { return view; } + auto getSampler() const -> vk::Sampler { return sampler; } + auto getInfo() const -> vk::DescriptorImageInfo { return {sampler, view, layout}; } + auto getMipLevels() const -> uint32_t { return mipLevels; } + auto getAspectMask() const -> vk::ImageAspectFlags { return aspect; } + auto getLayout() const -> vk::ImageLayout { return layout; } + auto getExtent() const -> vk::Extent3D { return extent; } + auto getFormat() const -> vk::Format { return format; } + auto getLayerCount() const -> uint32_t { return layerCount; } + auto getViewType() const -> vk::ImageViewType { return viewType; } + + // Ensure that data is pre-filled + // ImageLayout is implicitly shifted to ShaderReadOnlyOptimal + void generateMipmaps(const CommandBuffer& commandBuffer); + + // TODO: refactor these + static auto loadFromFile(const Context& context, + const std::filesystem::path& filepath, + uint32_t mipLevels = 1, + vk::Filter _filter = vk::Filter::eLinear, + vk::SamplerAddressMode _addressMode = vk::SamplerAddressMode::eRepeat) + -> ImageHandle; + + // mipmap is not supported + static auto loadFromFileHDR(const Context& context, const std::filesystem::path& filepath) + -> ImageHandle; + + static auto loadFromKTX(const Context& context, const std::filesystem::path& filepath) + -> ImageHandle; + +private: + void createImageView(vk::ImageViewType _viewType, vk::ImageAspectFlags _aspect) { viewType = _viewType; aspect = _aspect; @@ -74,9 +122,9 @@ class Image { view = context->getDevice().createImageView(viewInfo); } - void createSampler(vk::Filter _filter = vk::Filter::eLinear, - vk::SamplerAddressMode _addressMode = vk::SamplerAddressMode::eRepeat, - vk::SamplerMipmapMode _mipmapMode = vk::SamplerMipmapMode::eLinear) { + void createSampler(vk::Filter _filter, + vk::SamplerAddressMode _addressMode, + vk::SamplerMipmapMode _mipmapMode) { vk::SamplerCreateInfo samplerInfo; samplerInfo.setMagFilter(_filter); samplerInfo.setMinFilter(_filter); @@ -95,38 +143,6 @@ class Image { sampler = context->getDevice().createSampler(samplerInfo); } - auto getImage() const -> vk::Image { return image; } - auto getView() const -> vk::ImageView { return view; } - auto getSampler() const -> vk::Sampler { return sampler; } - auto getInfo() const -> vk::DescriptorImageInfo { return {sampler, view, layout}; } - auto getMipLevels() const -> uint32_t { return mipLevels; } - auto getAspectMask() const -> vk::ImageAspectFlags { return aspect; } - auto getLayout() const -> vk::ImageLayout { return layout; } - auto getExtent() const -> vk::Extent3D { return extent; } - auto getFormat() const -> vk::Format { return format; } - auto getLayerCount() const -> uint32_t { return layerCount; } - auto getViewType() const -> vk::ImageViewType { return viewType; } - - // Ensure that data is pre-filled - // ImageLayout is implicitly shifted to ShaderReadOnlyOptimal - void generateMipmaps(const CommandBuffer& commandBuffer); - - // TODO: refactor these - static auto loadFromFile(const Context& context, - const std::filesystem::path& filepath, - uint32_t mipLevels = 1, - vk::Filter _filter = vk::Filter::eLinear, - vk::SamplerAddressMode _addressMode = vk::SamplerAddressMode::eRepeat) - -> ImageHandle; - - // mipmap is not supported - static auto loadFromFileHDR(const Context& context, const std::filesystem::path& filepath) - -> ImageHandle; - - static auto loadFromKTX(const Context& context, const std::filesystem::path& filepath) - -> ImageHandle; - -private: const Context* context = nullptr; std::string debugName; diff --git a/src/Graphics/Image.cpp b/src/Graphics/Image.cpp index 282773d..9854ce0 100644 --- a/src/Graphics/Image.cpp +++ b/src/Graphics/Image.cpp @@ -22,8 +22,6 @@ Image::Image(const Context& _context, const ImageCreateInfo& createInfo) extent{createInfo.extent}, format{createInfo.format}, mipLevels{createInfo.mipLevels} { - vk::ImageType type = extent.depth == 1 ? vk::ImageType::e2D : vk::ImageType::e3D; - // Compute mipmap level if (mipLevels == std::numeric_limits::max()) { mipLevels = calculateMipLevels(extent.width, extent.height); @@ -32,7 +30,7 @@ Image::Image(const Context& _context, const ImageCreateInfo& createInfo) // NOTE: initialLayout must be Undefined or PreInitialized // NOTE: queueFamily is ignored if sharingMode is not concurrent vk::ImageCreateInfo imageInfo; - imageInfo.setImageType(type); + imageInfo.setImageType(createInfo.imageType); imageInfo.setFormat(format); imageInfo.setExtent(extent); imageInfo.setMipLevels(mipLevels); @@ -52,6 +50,37 @@ Image::Image(const Context& _context, const ImageCreateInfo& createInfo) context->getDevice().bindImageMemory(image, memory, 0); + // Image view + if (createInfo.viewInfo.has_value()) { + switch (createInfo.imageType) { + case vk::ImageType::e1D: { + viewType = vk::ImageViewType::e1D; + break; + } + case vk::ImageType::e2D: { + viewType = vk::ImageViewType::e2D; + break; + } + case vk::ImageType::e3D: { + viewType = vk::ImageViewType::e3D; + break; + } + default: { + viewType = {}; + break; + } + } + createImageView(viewType, createInfo.viewInfo.value().aspect); + } + + // Sampler + if (createInfo.samplerInfo.has_value()) { + createSampler(createInfo.samplerInfo.value().filter, + createInfo.samplerInfo.value().addressMode, + createInfo.samplerInfo.value().mipmapMode); + } + + // Debug if (!debugName.empty()) { context->setDebugName(image, createInfo.debugName.c_str()); } @@ -113,10 +142,14 @@ ImageHandle Image::loadFromFile(const Context& context, .extent = {static_cast(width), static_cast(height), 1}, .format = vk::Format::eR8G8B8A8Unorm, .mipLevels = mipLevels, + .viewInfo = ImageViewCreateInfo{}, + .samplerInfo = + SamplerCreateInfo{ + .filter = filter, + .addressMode = addressMode, + }, .debugName = filepathStr, }); - image->createImageView(); - image->createSampler(filter, addressMode); // Copy to image BufferHandle stagingBuffer = context.createBuffer({ @@ -160,9 +193,9 @@ ImageHandle Image::loadFromFileHDR(const Context& context, const std::filesystem .usage = ImageUsage::Sampled, .extent = {static_cast(width), static_cast(height), 1}, .format = vk::Format::eR32G32B32A32Sfloat, + .viewInfo = ImageViewCreateInfo{}, + .samplerInfo = SamplerCreateInfo{}, }); - image->createImageView(); - image->createSampler(); // Copy to image BufferHandle stagingBuffer = context.createBuffer({ @@ -221,8 +254,10 @@ auto Image::loadFromKTX(const Context& context, const std::filesystem::path& fil static_cast(texture.viewType), // texture.width, texture.height, texture.depth, // texture.levelCount, texture.layerCount); - image->createImageView(static_cast(texture.viewType)); - image->createSampler(); + image->createImageView(static_cast(texture.viewType), + vk::ImageAspectFlagBits::eColor); + image->createSampler(vk::Filter::eLinear, vk::SamplerAddressMode::eRepeat, + vk::SamplerMipmapMode ::eNearest); ktxTexture_Destroy(kTexture); ktxVulkanDeviceInfo_Destruct(&kvdi);