Add GEMipmapGenerator
This commit is contained in:
99
lib/graphics_engine/src/ge_mipmap_generator.hpp
Normal file
99
lib/graphics_engine/src/ge_mipmap_generator.hpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef HEADER_GE_MIPMAP_GENERATOR_HPP
|
||||
#define HEADER_GE_MIPMAP_GENERATOR_HPP
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <mipmap/img.h>
|
||||
#include <mipmap/imgresize.h>
|
||||
}
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "dimension2d.h"
|
||||
|
||||
namespace GE
|
||||
{
|
||||
struct GEImageLevel
|
||||
{
|
||||
irr::core::dimension2du m_dim;
|
||||
unsigned m_size;
|
||||
void* m_data;
|
||||
};
|
||||
|
||||
class GEMipmapGenerator
|
||||
{
|
||||
private:
|
||||
imMipmapCascade* m_cascade;
|
||||
|
||||
unsigned m_mipmap_sizes;
|
||||
|
||||
std::vector<GEImageLevel> m_levels;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void freeMipmapCascade()
|
||||
{
|
||||
if (m_cascade)
|
||||
{
|
||||
imFreeMipmapCascade(m_cascade);
|
||||
delete m_cascade;
|
||||
m_cascade = NULL;
|
||||
}
|
||||
}
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
GEMipmapGenerator(uint8_t* texture, unsigned channels,
|
||||
const irr::core::dimension2d<irr::u32>& size,
|
||||
bool normal_map)
|
||||
{
|
||||
m_cascade = new imMipmapCascade();
|
||||
unsigned width = size.Width;
|
||||
unsigned height = size.Height;
|
||||
m_levels.push_back({ size, width * height * channels, texture });
|
||||
|
||||
m_mipmap_sizes = 0;
|
||||
while (true)
|
||||
{
|
||||
width = width < 2 ? 1 : width >> 1;
|
||||
height = height < 2 ? 1 : height >> 1;
|
||||
const unsigned cur_mipmap_size = width * height * channels;
|
||||
m_levels.push_back({ irr::core::dimension2du(width, height),
|
||||
cur_mipmap_size, NULL });
|
||||
m_mipmap_sizes += cur_mipmap_size;
|
||||
if (width == 1 && height == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
imReduceOptions options;
|
||||
imReduceSetOptions(&options, normal_map ?
|
||||
IM_REDUCE_FILTER_NORMALMAP: IM_REDUCE_FILTER_LINEAR/*filter*/,
|
||||
2/*hopcount*/, 2.0f/*alpha*/, 1.0f/*amplifynormal*/,
|
||||
0.0f/*normalsustainfactor*/);
|
||||
|
||||
#ifdef DEBUG
|
||||
int ret = imBuildMipmapCascade(m_cascade, texture,
|
||||
m_levels[0].m_dim.Width, m_levels[0].m_dim.Height, 1/*layercount*/,
|
||||
channels, m_levels[0].m_dim.Width * channels, &options, 0);
|
||||
if (ret != 1)
|
||||
throw std::runtime_error("imBuildMipmapCascade failed");
|
||||
#else
|
||||
imBuildMipmapCascade(m_cascade, texture, m_levels[0].m_dim.Width,
|
||||
m_levels[0].m_dim.Height, 1/*layercount*/, channels,
|
||||
m_levels[0].m_dim.Width * channels, &options, 0);
|
||||
#endif
|
||||
for (unsigned int i = 1; i < m_levels.size(); i++)
|
||||
m_levels[i].m_data = m_cascade->mipmap[i];
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
~GEMipmapGenerator() { freeMipmapCascade(); }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getMipmapSizes() const { return m_mipmap_sizes; }
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<GEImageLevel>& getAllLevels() { return m_levels; }
|
||||
}; // GEMipmapGenerator
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "ge_vulkan_texture.hpp"
|
||||
|
||||
#include "ge_main.hpp"
|
||||
#include "ge_mipmap_generator.hpp"
|
||||
#include "ge_texture.hpp"
|
||||
#include "ge_vulkan_command_loader.hpp"
|
||||
#include "ge_vulkan_features.hpp"
|
||||
#include "ge_vulkan_driver.hpp"
|
||||
#include "ge_gl_utils.hpp"
|
||||
#include "ge_main.hpp"
|
||||
#include "ge_texture.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -112,22 +112,18 @@ GEVulkanTexture::~GEVulkanTexture()
|
||||
bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
|
||||
bool generate_hq_mipmap)
|
||||
{
|
||||
video::IImage* mipmap = NULL;
|
||||
std::vector<std::pair<core::dimension2du, unsigned> > mipmap_sizes =
|
||||
getMipmapSizes();
|
||||
VkDeviceSize mipmap_data_size = 0;
|
||||
GEMipmapGenerator* mipmap_generator = NULL;
|
||||
|
||||
unsigned channels = (isSingleChannel() ? 1 : 4);
|
||||
VkDeviceSize image_size = m_size.Width * m_size.Height * channels;
|
||||
if (generate_hq_mipmap)
|
||||
{
|
||||
mipmap = getDriver()->createImage(video::ECF_A8R8G8B8,
|
||||
mipmap_sizes[0].first);
|
||||
if (mipmap == NULL)
|
||||
throw std::runtime_error("Creating mipmap memory failed");
|
||||
generateHQMipmap(texture_data, mipmap_sizes, (uint8_t*)mipmap->lock());
|
||||
mipmap_data_size = mipmap_sizes.back().second +
|
||||
mipmap_sizes.back().first.getArea() * channels - image_size;
|
||||
const bool normal_map = (std::string(NamedPath.getPtr()).find(
|
||||
"_Normal.") != std::string::npos);
|
||||
mipmap_generator = new GEMipmapGenerator(texture_data, channels,
|
||||
m_size, normal_map);
|
||||
mipmap_data_size = mipmap_generator->getMipmapSizes();
|
||||
}
|
||||
|
||||
VkDeviceSize image_total_size = image_size + mipmap_data_size;
|
||||
@@ -151,12 +147,17 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
|
||||
if ((ret = vmaMapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation,
|
||||
(void**)&data)) != VK_SUCCESS)
|
||||
goto destroy;
|
||||
memcpy(data, texture_data, image_size);
|
||||
if (mipmap)
|
||||
|
||||
if (mipmap_generator)
|
||||
{
|
||||
memcpy(data + image_size, mipmap->lock(), mipmap_data_size);
|
||||
mipmap->drop();
|
||||
for (GEImageLevel& level : mipmap_generator->getAllLevels())
|
||||
{
|
||||
memcpy(data, level.m_data, level.m_size);
|
||||
data += level.m_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
memcpy(data, texture_data, image_size);
|
||||
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation);
|
||||
vmaFlushAllocation(m_vk->getVmaAllocator(),
|
||||
staging_buffer_allocation, 0, image_total_size);
|
||||
@@ -174,13 +175,16 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
|
||||
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
if (generate_hq_mipmap)
|
||||
if (mipmap_generator)
|
||||
{
|
||||
unsigned level = 0;
|
||||
for (auto& mip : mipmap_sizes)
|
||||
unsigned offset = 0;
|
||||
std::vector<GEImageLevel>& levels = mipmap_generator->getAllLevels();
|
||||
for (unsigned i = 0; i < levels.size(); i++)
|
||||
{
|
||||
copyBufferToImage(command_buffer, staging_buffer, mip.first.Width,
|
||||
mip.first.Height, 0, 0, mip.second, level++);
|
||||
GEImageLevel& level = levels[i];
|
||||
copyBufferToImage(command_buffer, staging_buffer,
|
||||
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, i);
|
||||
offset += level.m_size;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -195,6 +199,7 @@ bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
|
||||
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
|
||||
|
||||
destroy:
|
||||
delete mipmap_generator;
|
||||
vmaDestroyBuffer(m_vk->getVmaAllocator(), staging_buffer,
|
||||
staging_buffer_allocation);
|
||||
return ret == VK_SUCCESS;
|
||||
@@ -720,41 +725,4 @@ void GEVulkanTexture::reload()
|
||||
}
|
||||
} // reload
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void GEVulkanTexture::generateHQMipmap(void* in,
|
||||
std::vector<std::pair
|
||||
<core::dimension2du, unsigned> >& mms,
|
||||
uint8_t* out)
|
||||
{
|
||||
imMipmapCascade cascade;
|
||||
imReduceOptions options;
|
||||
imReduceSetOptions(&options,
|
||||
std::string(NamedPath.getPtr()).find("_Normal.") != std::string::npos ?
|
||||
IM_REDUCE_FILTER_NORMALMAP: IM_REDUCE_FILTER_LINEAR/*filter*/,
|
||||
2/*hopcount*/, 2.0f/*alpha*/, 1.0f/*amplifynormal*/,
|
||||
0.0f/*normalsustainfactor*/);
|
||||
|
||||
unsigned channels = (isSingleChannel() ? 1 : 4);
|
||||
#ifdef DEBUG
|
||||
int ret = imBuildMipmapCascade(&cascade, in, mms[0].first.Width,
|
||||
mms[0].first.Height, 1/*layercount*/, channels,
|
||||
mms[0].first.Width * channels, &options, 0);
|
||||
if (ret != 1)
|
||||
throw std::runtime_error("imBuildMipmapCascade failed");
|
||||
#else
|
||||
imBuildMipmapCascade(&cascade, in, mms[0].first.Width,
|
||||
mms[0].first.Height, 1/*layercount*/, channels,
|
||||
mms[0].first.Width * channels, &options, 0);
|
||||
#endif
|
||||
for (unsigned int i = 1; i < mms.size(); i++)
|
||||
{
|
||||
const unsigned copy_size = mms[i].first.getArea() * channels;
|
||||
memcpy(out, cascade.mipmap[i], copy_size);
|
||||
out += copy_size;
|
||||
mms[i].second = mms[i - 1].first.getArea() * channels +
|
||||
mms[i - 1].second;
|
||||
}
|
||||
imFreeMipmapCascade(&cascade);
|
||||
} // generateHQMipmap
|
||||
|
||||
}
|
||||
|
||||
@@ -77,30 +77,6 @@ protected:
|
||||
// ------------------------------------------------------------------------
|
||||
uint8_t* getTextureData();
|
||||
// ------------------------------------------------------------------------
|
||||
std::vector<std::pair<core::dimension2du, unsigned> > getMipmapSizes()
|
||||
{
|
||||
std::vector<std::pair<core::dimension2du, unsigned> > mipmap_sizes;
|
||||
unsigned width = m_size.Width;
|
||||
unsigned height = m_size.Height;
|
||||
mipmap_sizes.emplace_back(core::dimension2du(width, height),
|
||||
0);
|
||||
while (true)
|
||||
{
|
||||
width = width < 2 ? 1 : width >> 1;
|
||||
height = height < 2 ? 1 : height >> 1;
|
||||
mipmap_sizes.emplace_back(core::dimension2du(width, height), 0);
|
||||
if (width == 1 && height == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mipmap_sizes;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void generateHQMipmap(void* in,
|
||||
std::vector<std::pair<core::dimension2du,
|
||||
unsigned> >& mms, uint8_t* out);
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getMipmapLevels() const
|
||||
{
|
||||
if (!m_has_mipmaps)
|
||||
|
||||
Reference in New Issue
Block a user