SuperTuxKart 1.5 upstream source (from official release tarball)

This commit is contained in:
Benjamin
2026-06-11 20:04:02 +02:00
commit 2957e51aaa
8551 changed files with 1801800 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
#include "ge_compressor_astc_4x4.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <vector>
#ifdef ENABLE_LIBASTCENC
#include <astcenc.h>
#include <SDL_cpuinfo.h>
#endif
namespace GE
{
// ============================================================================
#ifdef ENABLE_LIBASTCENC
namespace GEVulkanFeatures
{
extern bool g_supports_astc_4x4;
}
std::vector<astcenc_context*> g_astc_contexts;
#endif
// ============================================================================
void GECompressorASTC4x4::init()
{
#ifdef ENABLE_LIBASTCENC
if (!GEVulkanFeatures::g_supports_astc_4x4)
return;
// Check for neon existence because libastcenc doesn't do that
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined (_M_ARM64)
if (SDL_HasNEON() == SDL_FALSE)
return;
#else
if (SDL_HasSSE41() == SDL_FALSE)
return;
#endif
astcenc_config cfg = {};
float quality = ASTCENC_PRE_FASTEST;
if (astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, quality, 0, &cfg) !=
ASTCENC_SUCCESS)
return;
for (unsigned i = 0; i < GEVulkanCommandLoader::getLoaderCount(); i++)
{
astcenc_context* context = NULL;
if (astcenc_context_alloc(&cfg, 1, &context) != ASTCENC_SUCCESS)
{
destroy();
return;
}
g_astc_contexts.push_back(context);
}
#endif
} // init
// ============================================================================
void GECompressorASTC4x4::destroy()
{
#ifdef ENABLE_LIBASTCENC
for (astcenc_context* context : g_astc_contexts)
astcenc_context_free(context);
g_astc_contexts.clear();
#endif
} // destroy
// ============================================================================
bool GECompressorASTC4x4::loaded()
{
#ifdef ENABLE_LIBASTCENC
return !g_astc_contexts.empty();
#else
return false;
#endif
} // loaded
// ----------------------------------------------------------------------------
GECompressorASTC4x4::GECompressorASTC4x4(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map)
: GEMipmapGenerator(texture, channels, size, normal_map)
{
m_compressed_data = NULL;
#ifdef ENABLE_LIBASTCENC
assert(channels == 4);
size_t total_size = 0;
m_mipmap_sizes = 0;
for (unsigned i = 0; i < m_levels.size(); i++)
{
GEImageLevel& level = m_levels[i];
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
total_size += cur_size;
if (i > 0)
m_mipmap_sizes += cur_size;
}
std::vector<GEImageLevel> compressed_levels;
m_compressed_data = new uint8_t[total_size];
uint8_t* cur_offset = m_compressed_data;
for (GEImageLevel& level : m_levels)
{
astcenc_image img;
img.dim_x = level.m_dim.Width;
img.dim_y = level.m_dim.Height;
img.dim_z = 1;
img.data_type = ASTCENC_TYPE_U8;
img.data = &level.m_data;
astcenc_swizzle swizzle;
swizzle.r = ASTCENC_SWZ_R;
swizzle.g = ASTCENC_SWZ_G;
swizzle.b = ASTCENC_SWZ_B;
swizzle.a = ASTCENC_SWZ_A;
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
if (astcenc_compress_image(
g_astc_contexts[GEVulkanCommandLoader::getLoaderId()], &img,
&swizzle, cur_offset, cur_size, 0) != ASTCENC_SUCCESS)
printf("astcenc_compress_image failed!\n");
compressed_levels.push_back({ level.m_dim, cur_size, cur_offset });
cur_offset += cur_size;
}
freeMipmapCascade();
std::swap(compressed_levels, m_levels);
#endif
} // GECompressorASTC4x4
}
@@ -0,0 +1,29 @@
#ifndef HEADER_GE_ASTC_COMPRESSOR_HPP
#define HEADER_GE_ASTC_COMPRESSOR_HPP
#include "ge_mipmap_generator.hpp"
namespace GE
{
class GECompressorASTC4x4 : public GEMipmapGenerator
{
private:
uint8_t* m_compressed_data;
public:
// ------------------------------------------------------------------------
static void init();
// ------------------------------------------------------------------------
static void destroy();
// ------------------------------------------------------------------------
static bool loaded();
// ------------------------------------------------------------------------
GECompressorASTC4x4(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map);
// ------------------------------------------------------------------------
~GECompressorASTC4x4() { delete [] m_compressed_data; }
}; // GEASTCCompressor
}
#endif
@@ -0,0 +1,105 @@
#ifdef LLVM_MINGW_FLTUSED
// From https://stackoverflow.com/questions/1583196/building-visual-c-app-that-doesnt-use-crt-functions-still-references-some
#ifdef __cplusplus
extern "C" {
#endif
// It should be a single underscore since the double one is the mangled name
int _fltused = 0;
#ifdef __cplusplus
}
#endif
#endif
#include "ge_compressor_bptc_bc7.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_features.hpp"
#ifdef BC7_ISPC
#include <bc7e_ispc.h>
#endif
#include <algorithm>
#include <cassert>
namespace GE
{
// ============================================================================
void GECompressorBPTCBC7::init()
{
#ifdef BC7_ISPC
if (!GEVulkanFeatures::supportsBPTCBC7())
return;
ispc::bc7e_compress_block_init();
#endif
} // init
// ----------------------------------------------------------------------------
GECompressorBPTCBC7::GECompressorBPTCBC7(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map)
: GEMipmapGenerator(texture, channels, size, normal_map)
{
m_compressed_data = NULL;
#ifdef BC7_ISPC
assert(channels == 4);
size_t total_size = 0;
m_mipmap_sizes = 0;
for (unsigned i = 0; i < m_levels.size(); i++)
{
GEImageLevel& level = m_levels[i];
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
total_size += cur_size;
if (i > 0)
m_mipmap_sizes += cur_size;
}
ispc::bc7e_compress_block_params p = {};
ispc::bc7e_compress_block_params_init_ultrafast(&p, true/*perceptual*/);
std::vector<GEImageLevel> compressed_levels;
m_compressed_data = new uint8_t[total_size];
uint8_t* cur_offset = m_compressed_data;
for (GEImageLevel& level : m_levels)
{
uint8_t* out = cur_offset;
for (unsigned y = 0; y < level.m_dim.Height; y += 4)
{
for (unsigned x = 0; x < level.m_dim.Width; x += 4)
{
// build the 4x4 block of pixels
uint32_t source_rgba[16] = {};
uint8_t* target_pixel = (uint8_t*)source_rgba;
for (unsigned py = 0; py < 4; py++)
{
for (unsigned px = 0; px < 4; px++)
{
// get the source pixel in the image
unsigned sx = x + px;
unsigned sy = y + py;
// enable if we're in the image
if (sx < level.m_dim.Width && sy < level.m_dim.Height)
{
uint8_t* rgba = (uint8_t*)level.m_data;
const unsigned pitch = level.m_dim.Width * 4;
uint8_t* source_pixel = rgba + pitch * sy + 4 * sx;
memcpy(target_pixel, source_pixel, 4);
}
// advance to the next pixel
target_pixel += 4;
}
}
ispc::bc7e_compress_blocks(1, (uint64_t*)out, source_rgba, &p);
out += 16;
}
}
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
compressed_levels.push_back({ level.m_dim, cur_size, cur_offset });
cur_offset += cur_size;
}
freeMipmapCascade();
std::swap(compressed_levels, m_levels);
#endif
} // GECompressorBPTCBC7
}
@@ -0,0 +1,25 @@
#ifndef HEADER_GE_COMPRESSOR_BPTC_BC7_HPP
#define HEADER_GE_COMPRESSOR_BPTC_BC7_HPP
#include "ge_mipmap_generator.hpp"
namespace GE
{
class GECompressorBPTCBC7 : public GEMipmapGenerator
{
private:
uint8_t* m_compressed_data;
public:
// ------------------------------------------------------------------------
static void init();
// ------------------------------------------------------------------------
GECompressorBPTCBC7(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map);
// ------------------------------------------------------------------------
~GECompressorBPTCBC7() { delete [] m_compressed_data; }
}; // GECompressorBPTCBC7
}
#endif
@@ -0,0 +1,98 @@
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_main.hpp"
#include <algorithm>
#include <cassert>
#include <squish.h>
static_assert(squish::kColourClusterFit == (1 << 5), "Wrong header");
static_assert(squish::kColourRangeFit == (1 << 6), "Wrong header");
static_assert(squish::kColourIterativeClusterFit == (1 << 8), "Wrong header");
// ============================================================================
extern "C" void squishCompressImage(uint8_t* rgba, int width, int height,
int pitch, void* blocks, unsigned flags)
{
// This function is copied from CompressImage in libsquish to avoid omp
// if enabled by shared libsquish, because we are already using
// multiple thread
for (int y = 0; y < height; y += 4)
{
// initialise the block output
uint8_t* target_block = reinterpret_cast<uint8_t*>(blocks);
target_block += ((y >> 2) * ((width + 3) >> 2)) * 16;
for (int x = 0; x < width; x += 4)
{
// build the 4x4 block of pixels
uint8_t source_rgba[16 * 4];
uint8_t* target_pixel = source_rgba;
int mask = 0;
for (int py = 0; py < 4; py++)
{
for (int px = 0; px < 4; px++)
{
// get the source pixel in the image
int sx = x + px;
int sy = y + py;
// enable if we're in the image
if (sx < width && sy < height)
{
// copy the rgba value
uint8_t* source_pixel = rgba + pitch * sy + 4 * sx;
memcpy(target_pixel, source_pixel, 4);
// enable this pixel
mask |= (1 << (4 * py + px));
}
// advance to the next pixel
target_pixel += 4;
}
}
// compress it into the output
squish::CompressMasked(source_rgba, mask, target_block, flags);
// advance
target_block += 16;
}
}
} // squishCompressImage
namespace GE
{
// ----------------------------------------------------------------------------
GECompressorS3TCBC3::GECompressorS3TCBC3(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map)
: GEMipmapGenerator(texture, channels, size, normal_map)
{
assert(channels == 4);
size_t total_size = 0;
m_mipmap_sizes = 0;
for (unsigned i = 0; i < m_levels.size(); i++)
{
GEImageLevel& level = m_levels[i];
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
total_size += cur_size;
if (i > 0)
m_mipmap_sizes += cur_size;
}
std::vector<GEImageLevel> compressed_levels;
m_compressed_data = new uint8_t[total_size];
uint8_t* cur_offset = m_compressed_data;
const unsigned tc_flag = squish::kDxt5 | squish::kColourRangeFit;
for (GEImageLevel& level : m_levels)
{
squishCompressImage((uint8_t*)level.m_data, level.m_dim.Width,
level.m_dim.Height, level.m_dim.Width * channels,
cur_offset, tc_flag);
unsigned cur_size = get4x4CompressedTextureSize(level.m_dim.Width,
level.m_dim.Height);
compressed_levels.push_back({ level.m_dim, cur_size, cur_offset });
cur_offset += cur_size;
}
freeMipmapCascade();
std::swap(compressed_levels, m_levels);
} // GECompressorS3TCBC3
}
@@ -0,0 +1,23 @@
#ifndef HEADER_GE_COMPRESSOR_S3TC_BC3_HPP
#define HEADER_GE_COMPRESSOR_S3TC_BC3_HPP
#include "ge_mipmap_generator.hpp"
namespace GE
{
class GECompressorS3TCBC3 : public GEMipmapGenerator
{
private:
uint8_t* m_compressed_data;
public:
// ------------------------------------------------------------------------
GECompressorS3TCBC3(uint8_t* texture, unsigned channels,
const irr::core::dimension2d<irr::u32>& size,
bool normal_map);
// ------------------------------------------------------------------------
~GECompressorS3TCBC3() { delete [] m_compressed_data; }
}; // GECompressorS3TCBC3
}
#endif
@@ -0,0 +1,75 @@
#include "ge_culling_tool.hpp"
#include "ge_main.hpp"
#include "ge_spm_buffer.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
#include "ISceneNode.h"
namespace GE
{
// ----------------------------------------------------------------------------
void GECullingTool::init(GEVulkanCameraSceneNode* cam)
{
mathPlaneFrustumf(&m_frustum[0].X, cam->getPVM());
m_cam_bbox = cam->getViewFrustum()->getBoundingBox();
} // init
// ----------------------------------------------------------------------------
bool GECullingTool::isCulled(const irr::core::vector3df& center, float radius)
{
for (int i = 0; i < 6; i++)
{
irr::core::quaternion q(center.X, center.Y, center.Z, 1.0f);
if (m_frustum[i].dotProduct(q) < -radius)
return true;
}
return false;
} // isCulled
// ----------------------------------------------------------------------------
bool GECullingTool::isCulled(irr::core::aabbox3df& bb)
{
if (!m_cam_bbox.intersectsWithBox(bb))
return true;
using namespace irr;
using namespace core;
quaternion edges[8] =
{
quaternion(bb.MinEdge.X, bb.MinEdge.Y, bb.MinEdge.Z, 1.0f),
quaternion(bb.MaxEdge.X, bb.MinEdge.Y, bb.MinEdge.Z, 1.0f),
quaternion(bb.MinEdge.X, bb.MaxEdge.Y, bb.MinEdge.Z, 1.0f),
quaternion(bb.MaxEdge.X, bb.MaxEdge.Y, bb.MinEdge.Z, 1.0f),
quaternion(bb.MinEdge.X, bb.MinEdge.Y, bb.MaxEdge.Z, 1.0f),
quaternion(bb.MaxEdge.X, bb.MinEdge.Y, bb.MaxEdge.Z, 1.0f),
quaternion(bb.MinEdge.X, bb.MaxEdge.Y, bb.MaxEdge.Z, 1.0f),
quaternion(bb.MaxEdge.X, bb.MaxEdge.Y, bb.MaxEdge.Z, 1.0f)
};
for (int i = 0; i < 6; i++)
{
bool culled = true;
for (int j = 0; j < 8; j++)
{
if (m_frustum[i].dotProduct(edges[j]) >= 0.0)
{
culled = false;
break;
}
}
if (culled)
return true;
}
return false;
} // isCulled
// ----------------------------------------------------------------------------
bool GECullingTool::isCulled(GESPMBuffer* buffer, irr::scene::ISceneNode* node)
{
irr::core::aabbox3df bb = buffer->getBoundingBox();
node->getAbsoluteTransformation().transformBoxEx(bb);
return isCulled(bb);
} // isCulled
}
@@ -0,0 +1,37 @@
#ifndef HEADER_GE_CULLING_TOOL_HPP
#define HEADER_GE_CULLING_TOOL_HPP
#include "aabbox3d.h"
#include "quaternion.h"
#include "matrix4.h"
namespace irr
{
namespace scene { class ISceneNode; }
}
namespace GE
{
class GESPMBuffer;
class GEVulkanCameraSceneNode;
class GECullingTool
{
private:
irr::core::quaternion m_frustum[6];
irr::core::aabbox3df m_cam_bbox;
public:
// ------------------------------------------------------------------------
void init(GEVulkanCameraSceneNode* cam);
// ------------------------------------------------------------------------
bool isCulled(irr::core::aabbox3df& bb);
// ------------------------------------------------------------------------
bool isCulled(const irr::core::vector3df& center, float radius);
// ------------------------------------------------------------------------
bool isCulled(GESPMBuffer* buffer, irr::scene::ISceneNode* node);
}; // GECullingTool
}
#endif
+229
View File
@@ -0,0 +1,229 @@
#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
#include "ge_dx9_texture.hpp"
#include "ge_main.hpp"
#include "ge_texture.hpp"
#include <IAttributes.h>
#include <vector>
namespace GE
{
GEDX9Texture::GEDX9Texture(const std::string& path,
std::function<void(video::IImage*)> image_mani)
: video::ITexture(path.c_str()), m_image_mani(image_mani),
m_device_9(NULL), m_texture_9(NULL), m_texture_size(0),
m_disable_reload(false)
{
getDevice9();
reload();
} // GEDX9Texture
// ----------------------------------------------------------------------------
GEDX9Texture::GEDX9Texture(video::IImage* img, const std::string& name)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_device_9(NULL), m_texture_9(NULL), m_texture_size(0),
m_disable_reload(true)
{
getDevice9();
if (!m_device_9 || !img)
{
LoadingFailed = true;
return;
}
uint8_t* data = NULL;
m_size = m_orig_size = img->getDimension();
HRESULT hr = m_device_9->CreateTexture(m_size.Width, m_size.Height,
0, D3DUSAGE_AUTOGENMIPMAP, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
&m_texture_9, NULL);
if (FAILED(hr))
{
LoadingFailed = true;
goto exit;
}
data = (uint8_t*)img->lock();
upload(data);
exit:
img->unlock();
img->drop();
} // GEDX9Texture
// ----------------------------------------------------------------------------
GEDX9Texture::GEDX9Texture(const std::string& name, unsigned int size)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_device_9(NULL), m_texture_9(NULL), m_texture_size(0),
m_disable_reload(true)
{
getDevice9();
if (!m_device_9)
{
LoadingFailed = true;
return;
}
m_orig_size.Width = size;
m_orig_size.Height = size;
m_size = m_orig_size;
HRESULT hr = m_device_9->CreateTexture(m_size.Width, m_size.Height,
0, D3DUSAGE_AUTOGENMIPMAP, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
&m_texture_9, NULL);
if (FAILED(hr))
{
LoadingFailed = true;
return;
}
std::vector<uint8_t> data;
data.resize(size * size * 4, 0);
upload(data.data());
} // GEDX9Texture
// ----------------------------------------------------------------------------
GEDX9Texture::~GEDX9Texture()
{
if (m_texture_9)
m_texture_9->Release();
if (m_device_9)
m_device_9->Release();
} // ~GEDX9Texture
// ----------------------------------------------------------------------------
void GEDX9Texture::getDevice9()
{
m_device_9 = GE::getDriver()->getExposedVideoData().D3D9.D3DDev9;
if (m_device_9)
m_device_9->AddRef();
} // getDevice9
// ----------------------------------------------------------------------------
void GEDX9Texture::reload()
{
if (m_disable_reload)
return;
if (!m_device_9)
{
LoadingFailed = true;
return;
}
const core::dimension2du& max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
video::IImage* texture_image = getResizedImage(NamedPath.getPtr(),
max_size, &m_orig_size);
if (texture_image == NULL)
{
LoadingFailed = true;
return;
}
m_size = texture_image->getDimension();
if (m_image_mani)
m_image_mani(texture_image);
if (m_texture_9 != NULL)
{
m_texture_9->Release();
m_texture_9 = NULL;
}
uint8_t* data = (uint8_t*)texture_image->lock();
HRESULT hr = m_device_9->CreateTexture(m_size.Width, m_size.Height,
0, D3DUSAGE_AUTOGENMIPMAP, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED,
&m_texture_9, NULL);
if (FAILED(hr))
{
LoadingFailed = true;
goto exit;
}
upload(data);
exit:
texture_image->unlock();
texture_image->drop();
} // reload
// ----------------------------------------------------------------------------
void GEDX9Texture::upload(uint8_t* data)
{
const unsigned int w = m_size.Width;
const unsigned int h = m_size.Height;
HRESULT hr;
D3DLOCKED_RECT rect;
hr = m_texture_9->LockRect(0, &rect, 0, 0);
if (FAILED(hr))
return;
uint8_t* dst = (uint8_t*)rect.pBits;
for (u32 i = 0; i < h; i++)
{
memcpy(dst, data, w * 4);
data += w * 4;
dst += rect.Pitch;
}
hr = m_texture_9->UnlockRect(0);
if (FAILED(hr))
return;
m_texture_9->GenerateMipSubLevels();
m_texture_size = w * h * 4;
} // upload
// ----------------------------------------------------------------------------
void* GEDX9Texture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
{
if (mode != video::ETLM_READ_ONLY || !m_texture_9)
return NULL;
HRESULT hr;
D3DLOCKED_RECT rect;
hr = m_texture_9->LockRect(0, &rect, 0,
(mode == video::ETLM_READ_ONLY) ? D3DLOCK_READONLY : 0);
if (FAILED(hr))
return NULL;
return rect.pBits;
} // lock
//-----------------------------------------------------------------------------
void GEDX9Texture::updateTexture(void* data, video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y)
{
if (!m_texture_9)
return;
std::vector<uint8_t> image_data;
uint8_t* src = NULL;
if (format == video::ECF_R8)
{
const unsigned int size = w * h;
image_data.resize(size * 4, 255);
uint8_t* orig_data = (uint8_t*)data;
for (unsigned int i = 0; i < size; i++)
image_data[4 * i + 3] = orig_data[i];
src = image_data.data();
}
else if (format == video::ECF_A8R8G8B8)
{
src = (uint8_t*)data;
}
if (src == NULL)
return;
HRESULT hr;
D3DLOCKED_RECT rect;
RECT subimg;
subimg.left = x;
subimg.top = y;
subimg.right = x + w;
subimg.bottom = y + h;
hr = m_texture_9->LockRect(0, &rect, &subimg, 0);
if (FAILED(hr))
return;
uint8_t* dst = (uint8_t*)rect.pBits;
for (u32 i = 0; i < h; i++)
{
memcpy(dst, src, w * 4);
src += w * 4;
dst += rect.Pitch;
}
hr = m_texture_9->UnlockRect(0);
if (FAILED(hr))
return;
m_texture_9->GenerateMipSubLevels();
} // updateTexture
}
#endif
@@ -0,0 +1,88 @@
#ifndef HEADER_GE_DX9_TEXTURE_HPP
#define HEADER_GE_DX9_TEXTURE_HPP
#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
#include <d3d9.h>
#include <functional>
#include <string>
#include <ITexture.h>
using namespace irr;
namespace GE
{
class GEDX9Texture : public video::ITexture
{
private:
core::dimension2d<u32> m_size, m_orig_size;
std::function<void(video::IImage*)> m_image_mani;
IDirect3DDevice9* m_device_9;
IDirect3DTexture9* m_texture_9;
unsigned int m_texture_size;
const bool m_disable_reload;
// ------------------------------------------------------------------------
void getDevice9();
// ------------------------------------------------------------------------
void upload(uint8_t* data);
public:
// ------------------------------------------------------------------------
GEDX9Texture(const std::string& path,
std::function<void(video::IImage*)> image_mani = nullptr);
// ------------------------------------------------------------------------
GEDX9Texture(video::IImage* img, const std::string& name);
// ------------------------------------------------------------------------
GEDX9Texture(const std::string& name, unsigned int size);
// ------------------------------------------------------------------------
virtual ~GEDX9Texture();
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0);
// ------------------------------------------------------------------------
virtual void unlock()
{
if (m_texture_9)
m_texture_9->UnlockRect(0);
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_orig_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return video::EDT_DIRECT3D9; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return true; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const { return (u64)m_texture_9; }
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
virtual void reload();
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y);
}; // GEDX9Texture
}
#endif
#endif
+240
View File
@@ -0,0 +1,240 @@
#include "ge_gl_texture.hpp"
#include "ge_gl_utils.hpp"
#include "ge_main.hpp"
#include "ge_texture.hpp"
#include <IAttributes.h>
#include <vector>
// TODO remove it after vulkan is done
namespace irr
{
namespace video { extern bool useCoreContext; }
}
namespace GE
{
GEGLTexture::GEGLTexture(const std::string& path,
std::function<void(video::IImage*)> image_mani)
: video::ITexture(path.c_str()), m_image_mani(image_mani),
m_locked_data(NULL), m_texture_name(0), m_texture_size(0),
m_driver_type(GE::getDriver()->getDriverType()),
m_disable_reload(false), m_single_channel(false)
{
reload();
} // GEGLTexture
// ----------------------------------------------------------------------------
GEGLTexture::GEGLTexture(video::IImage* img, const std::string& name)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_locked_data(NULL), m_texture_name(0), m_texture_size(0),
m_driver_type(GE::getDriver()->getDriverType()),
m_disable_reload(true), m_single_channel(false)
{
if (!img)
{
LoadingFailed = true;
return;
}
glGenTextures(1, &m_texture_name);
m_size = m_orig_size = img->getDimension();
uint8_t* data = (uint8_t*)img->lock();
upload(data);
img->unlock();
img->drop();
} // GEGLTexture
// ----------------------------------------------------------------------------
GEGLTexture::GEGLTexture(const std::string& name, unsigned int size,
bool single_channel)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_locked_data(NULL), m_texture_name(0), m_texture_size(0),
m_driver_type(GE::getDriver()->getDriverType()),
m_disable_reload(true), m_single_channel(false)
{
glGenTextures(1, &m_texture_name);
m_orig_size.Width = size;
m_orig_size.Height = size;
m_size = m_orig_size;
bool texture_swizzle = false;
if (m_driver_type == video::EDT_OGLES2)
{
int gl_major_version = 0;
glGetIntegerv(GL_MAJOR_VERSION, &gl_major_version);
if (gl_major_version >=3)
texture_swizzle = true;
else
texture_swizzle = false;
}
else
{
texture_swizzle = irr::video::useCoreContext &&
GE::hasGLExtension("GL_ARB_texture_swizzle");
}
if (single_channel && texture_swizzle)
m_single_channel = true;
std::vector<uint8_t> data;
data.resize(size * size * (m_single_channel ? 1 : 4), 0);
upload(data.data());
} // GEGLTexture
// ----------------------------------------------------------------------------
GEGLTexture::~GEGLTexture()
{
if (m_texture_name != 0)
glDeleteTextures(1, &m_texture_name);
} // ~GEGLTexture
// ----------------------------------------------------------------------------
void GEGLTexture::reload()
{
if (m_disable_reload)
return;
const core::dimension2du & max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
video::IImage* texture_image = getResizedImage(NamedPath.getPtr(),
max_size, &m_orig_size);
if (texture_image == NULL)
{
LoadingFailed = true;
return;
}
m_size = texture_image->getDimension();
if (m_image_mani)
m_image_mani(texture_image);
if (m_texture_name == 0)
glGenTextures(1, &m_texture_name);
uint8_t* data = (uint8_t*)texture_image->lock();
upload(data);
texture_image->unlock();
texture_image->drop();
} // reload
// ----------------------------------------------------------------------------
void GEGLTexture::upload(uint8_t* data)
{
const unsigned int w = m_size.Width;
const unsigned int h = m_size.Height;
unsigned int format = m_single_channel ? GL_RED : GL_BGRA;
unsigned int internal_format = m_single_channel ? GL_R8 : GL_RGBA8;
if (m_driver_type == video::EDT_OGLES2)
{
formatConversion(data, &format, w, h);
int gl_major_version = 0;
glGetIntegerv(GL_MAJOR_VERSION, &gl_major_version);
// GLES 2.0 specs doesn't allow GL_RGBA8 internal format
if (gl_major_version < 3)
internal_format = GL_RGBA;
}
glBindTexture(GL_TEXTURE_2D, m_texture_name);
if (m_single_channel)
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
}
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
GL_UNSIGNED_BYTE, data);
if (hasMipMaps())
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
m_texture_size = w * h * (m_single_channel ? 1 : 4);
} // upload
// ----------------------------------------------------------------------------
void* GEGLTexture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
{
if (mode != video::ETLM_READ_ONLY)
return NULL;
if (m_driver_type == video::EDT_OGLES2 || !glGetTexImage)
{
const core::dimension2du & max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
video::IImage* img = getResizedImage(NamedPath.getPtr(), max_size,
NULL, &m_size);
if (!img)
return NULL;
img->setDeleteMemory(false);
m_locked_data = (uint8_t*)img->lock();
img->unlock();
img->drop();
return m_locked_data;
}
m_locked_data = new uint8_t[m_size.Width * m_size.Height * 4]();
GLint tmp_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmp_texture);
glBindTexture(GL_TEXTURE_2D, m_texture_name);
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_locked_data);
glBindTexture(GL_TEXTURE_2D, tmp_texture);
return m_locked_data;
} // lock
//-----------------------------------------------------------------------------
void GEGLTexture::formatConversion(uint8_t* data, unsigned int* format,
unsigned int w, unsigned int h) const
{
if (!m_single_channel)
{
if (format)
*format = GL_RGBA;
for (unsigned int i = 0; i < w * h; i++)
{
uint8_t tmp_val = data[i * 4];
data[i * 4] = data[i * 4 + 2];
data[i * 4 + 2] = tmp_val;
}
}
} // formatConversion
//-----------------------------------------------------------------------------
void GEGLTexture::updateTexture(void* data, video::ECOLOR_FORMAT format, u32 w,
u32 h, u32 x, u32 y)
{
glBindTexture(GL_TEXTURE_2D, m_texture_name);
if (m_single_channel)
{
if (format == video::ECF_R8)
{
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RED,
GL_UNSIGNED_BYTE, data);
}
}
else
{
if (format == video::ECF_R8)
{
const unsigned int size = w * h;
std::vector<uint8_t> image_data(size * 4, 255);
uint8_t* orig_data = (uint8_t*)data;
for (unsigned int i = 0; i < size; i++)
image_data[4 * i + 3] = orig_data[i];
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
GL_UNSIGNED_BYTE, image_data.data());
}
else if (format == video::ECF_A8R8G8B8)
{
uint8_t* u8_data = (uint8_t*)data;
for (unsigned int i = 0; i < w * h; i++)
{
uint8_t tmp_val = u8_data[i * 4];
u8_data[i * 4] = u8_data[i * 4 + 2];
u8_data[i * 4 + 2] = tmp_val;
}
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
GL_UNSIGNED_BYTE, u8_data);
}
}
if (hasMipMaps())
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
} // updateTexture
}
+92
View File
@@ -0,0 +1,92 @@
#ifndef HEADER_GE_GL_TEXTURE_HPP
#define HEADER_GE_GL_TEXTURE_HPP
#include "glad/gl.h"
#include <functional>
#include <string>
#include <ITexture.h>
using namespace irr;
namespace GE
{
class GEGLTexture : public video::ITexture
{
private:
core::dimension2d<u32> m_size, m_orig_size;
std::function<void(video::IImage*)> m_image_mani;
uint8_t* m_locked_data;
GLuint m_texture_name;
unsigned int m_texture_size;
const video::E_DRIVER_TYPE m_driver_type;
const bool m_disable_reload;
bool m_single_channel;
// ------------------------------------------------------------------------
void upload(uint8_t* data);
// ------------------------------------------------------------------------
void formatConversion(uint8_t* data, unsigned int* format, unsigned int w,
unsigned int h) const;
public:
// ------------------------------------------------------------------------
GEGLTexture(const std::string& path,
std::function<void(video::IImage*)> image_mani = nullptr);
// ------------------------------------------------------------------------
GEGLTexture(video::IImage* img, const std::string& name);
// ------------------------------------------------------------------------
GEGLTexture(const std::string& name, unsigned int size,
bool single_channel);
// ------------------------------------------------------------------------
virtual ~GEGLTexture();
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0);
// ------------------------------------------------------------------------
virtual void unlock()
{
if (m_locked_data)
{
delete [] m_locked_data;
m_locked_data = NULL;
}
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_orig_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return m_driver_type; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return glGenerateMipmap != NULL; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const { return m_texture_name; }
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const { return m_texture_size; }
// ------------------------------------------------------------------------
virtual void reload();
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y);
}; // GEGLTexture
}
#endif
+236
View File
@@ -0,0 +1,236 @@
#include "ge_main.hpp"
#include "ge_occlusion_culling.hpp"
#include "ge_spm.hpp"
#include "ge_spm_buffer.hpp"
#include "ge_vulkan_driver.hpp"
#include "mini_glm.hpp"
#include "IMesh.h"
#include "IMeshBuffer.h"
#include "S3DVertex.h"
#include <algorithm>
#include <chrono>
#include <memory>
namespace GE
{
irr::video::IVideoDriver* g_driver = NULL;
GEConfig g_config =
{
false,
false,
false,
true,
false,
false,
true,
GADT_DISABLED,
GSSRT_DISABLED,
false,
{},
1.0f
};
std::string g_shader_folder = "";
std::chrono::steady_clock::time_point g_mono_start =
std::chrono::steady_clock::now();
std::unique_ptr<GEOcclusionCulling> g_occulsion_culling;
std::array<float, 4> g_displace_direction = {};
void setVideoDriver(irr::video::IVideoDriver* driver)
{
if (driver != g_driver)
{
// Reset everytime driver is recreated
g_config.m_ondemand_load_texture_paths.clear();
g_config.m_auto_deferred_type = GADT_DISABLED;
g_driver = driver;
}
}
irr::video::IVideoDriver* getDriver()
{
return g_driver;
}
GE::GEVulkanDriver* getVKDriver()
{
return dynamic_cast<GE::GEVulkanDriver*>(g_driver);
}
GEConfig* getGEConfig()
{
return &g_config;
}
void setShaderFolder(const std::string& path)
{
g_shader_folder = path + "ge_shaders/";
}
const std::string& getShaderFolder()
{
return g_shader_folder;
}
void deinit()
{
}
uint64_t getMonoTimeMs()
{
auto duration = std::chrono::steady_clock::now() - g_mono_start;
auto value =
std::chrono::duration_cast<std::chrono::milliseconds>(duration);
return value.count();
}
void mathPlaneNormf(float *p)
{
float f = 1.0f / sqrtf(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]);
p[0] *= f;
p[1] *= f;
p[2] *= f;
p[3] *= f;
}
void mathPlaneFrustumf(float* out, const irr::core::matrix4& pvm)
{
// return 6 planes, 24 floats
const float* m = pvm.pointer();
// near
out[0] = m[3] + m[2];
out[1] = m[7] + m[6];
out[2] = m[11] + m[10];
out[3] = m[15] + m[14];
mathPlaneNormf(&out[0]);
// right
out[4] = m[3] - m[0];
out[4 + 1] = m[7] - m[4];
out[4 + 2] = m[11] - m[8];
out[4 + 3] = m[15] - m[12];
mathPlaneNormf(&out[4]);
// left
out[2 * 4] = m[3] + m[0];
out[2 * 4 + 1] = m[7] + m[4];
out[2 * 4 + 2] = m[11] + m[8];
out[2 * 4 + 3] = m[15] + m[12];
mathPlaneNormf(&out[2 * 4]);
// bottom
out[3 * 4] = m[3] + m[1];
out[3 * 4 + 1] = m[7] + m[5];
out[3 * 4 + 2] = m[11] + m[9];
out[3 * 4 + 3] = m[15] + m[13];
mathPlaneNormf(&out[3 * 4]);
// top
out[4 * 4] = m[3] - m[1];
out[4 * 4 + 1] = m[7] - m[5];
out[4 * 4 + 2] = m[11] - m[9];
out[4 * 4 + 3] = m[15] - m[13];
mathPlaneNormf(&out[4 * 4]);
// far
out[5 * 4] = m[3] - m[2];
out[5 * 4 + 1] = m[7] - m[6];
out[5 * 4 + 2] = m[11] - m[10];
out[5 * 4 + 3] = m[15] - m[14];
mathPlaneNormf(&out[5 * 4]);
}
irr::scene::IAnimatedMesh* convertIrrlichtMeshToSPM(irr::scene::IMesh* mesh)
{
GESPM* spm = new GESPM();
for (unsigned i = 0; i < mesh->getMeshBufferCount(); i++)
{
std::vector<video::S3DVertexSkinnedMesh> vertices;
scene::IMeshBuffer* mb = mesh->getMeshBuffer(i);
if (!mb)
continue;
GESPMBuffer* spm_mb = new GESPMBuffer();
assert(mb->getVertexType() == video::EVT_STANDARD);
video::S3DVertex* v_ptr = (video::S3DVertex*)mb->getVertices();
for (unsigned j = 0; j < mb->getVertexCount(); j++)
{
video::S3DVertexSkinnedMesh sp;
sp.m_position = v_ptr[j].Pos;
sp.m_normal = MiniGLM::compressVector3(v_ptr[j].Normal);
video::SColorf orig(v_ptr[j].Color);
video::SColorf diffuse(mb->getMaterial().DiffuseColor);
orig.r = orig.r * diffuse.r;
orig.g = orig.g * diffuse.g;
orig.b = orig.b * diffuse.b;
orig.a = orig.a * diffuse.a;
sp.m_color = orig.toSColor();
sp.m_all_uvs[0] = MiniGLM::toFloat16(v_ptr[j].TCoords.X);
sp.m_all_uvs[1] = MiniGLM::toFloat16(v_ptr[j].TCoords.Y);
spm_mb->getVerticesVector().push_back(sp);
}
uint16_t* idx_ptr = mb->getIndices();
std::vector<uint16_t> indices(idx_ptr, idx_ptr + mb->getIndexCount());
std::swap(spm_mb->getIndicesVector(), indices);
spm_mb->getMaterial() = mb->getMaterial();
spm_mb->recalculateBoundingBox();
spm->addMeshBuffer(spm_mb);
}
spm->finalize();
return spm;
}
void copyToMappedBuffer(uint32_t* mapped, GESPMBuffer* spmb, size_t offset)
{
for (unsigned i = offset; i < spmb->getVertexCount(); i++)
{
auto& vv = spmb->getVerticesVector();
memcpy(mapped, &vv[i], 4 * sizeof(uint32_t));
mapped += 4;
if (getGEConfig()->m_pbr)
*mapped = srgb255ToLinearFromSColor(vv[i].m_color).color;
else
memcpy(mapped, &vv[i].m_color, sizeof(video::SColor));
mapped += 1;
memcpy(mapped, vv[i].m_all_uvs, 3 * sizeof(uint32_t));
mapped += 3;
}
}
GEOcclusionCulling* getOcclusionCulling()
{
if (!g_occulsion_culling)
{
g_occulsion_culling = std::unique_ptr<GEOcclusionCulling>(
new GEOcclusionCulling());
}
return g_occulsion_culling.get();
}
void resetOcclusionCulling()
{
g_occulsion_culling.reset();
}
bool hasOcclusionCulling()
{
if (g_occulsion_culling)
return true;
return false;
}
bool needsDeferredRendering(bool auto_deferred)
{
return g_config.m_pbr &&
((auto_deferred && g_config.m_auto_deferred_type != GADT_DISABLED) ||
g_config.m_force_deferred);
}
std::array<float, 4>& getDisplaceDirection()
{
return g_displace_direction;
}
}
@@ -0,0 +1,245 @@
#include "ge_material_manager.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "vector3d.h"
#include <algorithm>
#include <cstring>
#include <stdexcept>
#include <unordered_map>
#include "../source/Irrlicht/os.h"
namespace GE
{
// ============================================================================
namespace GEMaterialManager
{
std::vector<
std::pair<std::string, std::shared_ptr<const GEMaterial> > > g_materials;
irr::core::vector3df g_wind_direction;
std::unordered_map<std::string, std::shared_ptr<const GEMaterial> > g_mat_map;
std::unordered_map<std::string, irr::video::E_MATERIAL_TYPE> g_mat_id_map;
std::unordered_map<uint32_t, std::string> g_id_mat_map;
std::unordered_map<std::string, std::function<void(uint32_t*, void**)> >
g_default_push_constants =
{
{
"grass",
[](uint32_t* size, void** data)
{
*size = sizeof(irr::core::vector3df);
*data = &g_wind_direction;
}
},
{
"displace",
[](uint32_t* size, void** data)
{
*size = sizeof(getDisplaceDirection());
*data = getDisplaceDirection().data();
}
}
};
// ============================================================================
} // GEMaterialManager
// ----------------------------------------------------------------------------
std::string readString(io::IXMLReaderUTF8* xml)
{
if (xml->read() && xml->getNodeType() == io::EXN_TEXT)
return xml->getNodeData();
return "";
} // readString
// ----------------------------------------------------------------------------
bool readBool(io::IXMLReaderUTF8* xml)
{
return readString(xml) == "true";
} // readBool
// ============================================================================
void GEMaterialManager::init()
{
std::array<std::string, irr::video::EMT_MATERIAL_COUNT> def_mappings = {};
def_mappings[irr::video::EMT_SOLID] = "solid";
def_mappings[irr::video::EMT_NORMAL_MAP_SOLID] = "normalmap";
def_mappings[irr::video::EMT_SOLID_2_LAYER] = "decal";
def_mappings[irr::video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF] = "alphatest";
def_mappings[irr::video::EMT_STK_GRASS] = "grass";
def_mappings[irr::video::EMT_TRANSPARENT_ALPHA_CHANNEL] = "alphablend";
def_mappings[irr::video::EMT_TRANSPARENT_ADD_COLOR] = "additive";
uint32_t mapping_cursor = 0;
g_materials.clear();
g_mat_map.clear();
g_mat_id_map.clear();
g_id_mat_map.clear();
io::IXMLReaderUTF8* xml =
getDriver()->getFileSystem()->createXMLReaderUTF8(
(GE::getShaderFolder() + "shader_settings.xml").c_str());
if (!xml)
throw std::runtime_error("Could not load shader_settings.xml");
while (xml->read())
{
if (xml->getNodeType() == io::EXN_ELEMENT &&
!strcmp(xml->getNodeName(), "setting"))
{
GEMaterial settings;
std::string name = xml->getAttributeValue("name");
while (xml->read())
{
if (xml->getNodeType() == io::EXN_ELEMENT)
{
if (!strcmp(xml->getNodeName(), "properties"))
{
while (xml->read())
{
if (xml->getNodeType() == io::EXN_ELEMENT)
{
const char* node_name = xml->getNodeName();
if (!strcmp(node_name, "depth-write"))
settings.m_depth_write = readBool(xml);
else if (!strcmp(node_name, "depth-test"))
settings.m_depth_test = readBool(xml);
else if (!strcmp(node_name, "backface-culling"))
settings.m_backface_culling = readBool(xml);
else if (!strcmp(node_name, "nonpbr-fallback"))
settings.m_nonpbr_fallback = readString(xml);
else if (!strcmp(node_name, "alphablend"))
settings.m_alphablend = readBool(xml);
else if (!strcmp(node_name, "additive"))
settings.m_additive = readBool(xml);
else if (!strcmp(node_name, "srgb-settings"))
{
std::string srgb_str = readString(xml);
for (unsigned i = 0; i < std::min(srgb_str.size(),
settings.m_srgb_settings.size()); i++)
{
settings.m_srgb_settings[i] = (srgb_str[i] == 'Y');
}
}
}
else if (xml->getNodeType() == io::EXN_ELEMENT_END &&
!strcmp(xml->getNodeName(), "properties"))
break;
}
}
else if (!strcmp(xml->getNodeName(), "shaders"))
{
while (xml->read())
{
if (xml->getNodeType() == io::EXN_ELEMENT)
{
const char* node_name = xml->getNodeName();
if (!strcmp(node_name, "vertex"))
settings.m_vertex_shader = readString(xml);
else if (!strcmp(node_name, "fragment"))
settings.m_fragment_shader = readString(xml);
else if (!strcmp(node_name, "depth"))
settings.m_depth_only_fragment_shader = readString(xml);
else if (!strcmp(node_name, "skinning-vertex"))
settings.m_skinning_vertex_shader = readString(xml);
}
else if (xml->getNodeType() == io::EXN_ELEMENT_END &&
!strcmp(xml->getNodeName(), "shaders"))
break;
}
}
}
else if (xml->getNodeType() == io::EXN_ELEMENT_END &&
!strcmp(xml->getNodeName(), "setting"))
break;
}
if (g_default_push_constants.find(name) !=
g_default_push_constants.end())
{
settings.m_push_constants = g_default_push_constants.at(name);
}
bool found = false;
for (uint32_t i = 0; i < def_mappings.size(); i++)
{
if (def_mappings[i] == name)
{
g_mat_id_map[name] = (irr::video::E_MATERIAL_TYPE)i;
g_id_mat_map[i] = name;
found = true;
break;
}
}
if (!found)
{
while (mapping_cursor < def_mappings.size() &&
!def_mappings[mapping_cursor].empty())
{
mapping_cursor = mapping_cursor + 1;
}
if (mapping_cursor < def_mappings.size())
{
g_mat_id_map[name] = (irr::video::E_MATERIAL_TYPE)mapping_cursor;
g_id_mat_map[mapping_cursor] = name;
def_mappings[mapping_cursor] = name;
mapping_cursor = mapping_cursor + 1;
}
else
{
char msg[50] = {};
snprintf(msg, 50, "Too many materials which exceeded %u.",
irr::video::EMT_MATERIAL_COUNT);
os::Printer::log("GEMaterialManager", msg);
xml->drop();
return;
}
}
auto m = std::make_shared<const GEMaterial>(settings);
g_materials.emplace_back(name, m);
g_mat_map[name] = m;
}
}
xml->drop();
} // init
// ----------------------------------------------------------------------------
void GEMaterialManager::update()
{
g_wind_direction = irr::core::vector3df(1.0f, 0.0f, 0.0f) *
(getMonoTimeMs() / 1000.0f) * 1.5f;
} // update
// ----------------------------------------------------------------------------
irr::video::E_MATERIAL_TYPE
GEMaterialManager::getIrrMaterialType(const std::string& shader_name)
{
if (g_mat_id_map.find(shader_name) != g_mat_id_map.end())
return g_mat_id_map.at(shader_name);
return (irr::video::E_MATERIAL_TYPE)0;
} // getIrrMaterialType
// ----------------------------------------------------------------------------
const std::string& GEMaterialManager::getShader(irr::video::E_MATERIAL_TYPE mt)
{
uint32_t id = mt;
if (g_id_mat_map.find(id) != g_id_mat_map.end())
return g_id_mat_map.at(id);
return g_id_mat_map.at(0);
} // getShader
// ----------------------------------------------------------------------------
std::shared_ptr<const GEMaterial>
GEMaterialManager::getMaterial(const std::string& shader_name)
{
if (g_mat_map.find(shader_name) != g_mat_map.end())
return g_mat_map.at(shader_name);
return nullptr;
} // getMaterial
}
@@ -0,0 +1,100 @@
#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;
protected:
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];
}
// ------------------------------------------------------------------------
virtual ~GEMipmapGenerator() { freeMipmapCascade(); }
// ------------------------------------------------------------------------
unsigned getMipmapSizes() const { return m_mipmap_sizes; }
// ------------------------------------------------------------------------
std::vector<GEImageLevel>& getAllLevels() { return m_levels; }
}; // GEMipmapGenerator
}
#endif
@@ -0,0 +1,129 @@
#include "ge_occlusion_culling.hpp"
#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h>
#include <btBulletDynamicsCommon.h>
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace GE
{
// ============================================================================
bool GEOcclusionCulling::SpherePointGenerator::getNextPoint(btVector3& point)
{
if (m_current_point >= m_num_points)
return false;
if (m_current_point == 0)
{
point = m_center;
m_current_point++;
return true;
}
float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
float angle_increment = 2.0f * M_PI * golden_ratio;
float t = float(m_current_point) / float(m_num_points);
float inclination = acos(1.0f - 2.0f * t);
float azimuth = angle_increment * float(m_current_point);
float x = sin(inclination) * cos(azimuth);
float y = sin(inclination) * sin(azimuth);
float z = cos(inclination);
// Test points at 80% of the sphere's surface
float offset = 0.8f;
point = m_center + btVector3(x, y, z) * m_radius * offset;
m_current_point++;
return true;
} // GEOcclusionCulling::SpherePointGenerator::getNextPoint
// ----------------------------------------------------------------------------
GEOcclusionCulling::GEOcclusionCulling()
{
m_triangle_mesh = NULL;
m_occluder_shape = NULL;
m_occluder_object = NULL;
} // GEOcclusionCulling
// ----------------------------------------------------------------------------
GEOcclusionCulling::~GEOcclusionCulling()
{
delete m_occluder_object;
delete m_occluder_shape;
delete m_triangle_mesh;
} // ~GEOcclusionCulling
// ----------------------------------------------------------------------------
void GEOcclusionCulling::addOccluderMesh(
const std::vector<std::array<btVector3, 3> >& tris)
{
assert(m_triangle_mesh == NULL);
m_triangle_mesh = new btTriangleMesh();
for (auto& t : tris)
m_triangle_mesh->addTriangle(t[0], t[1], t[2]);
m_occluder_shape = new btBvhTriangleMeshShape(m_triangle_mesh,
false/*useQuantizedAabbCompression*/);
m_occluder_object = new btCollisionObject();
m_occluder_object->setCollisionShape(m_occluder_shape);
} // addOccluderMesh
// ----------------------------------------------------------------------------
bool GEOcclusionCulling::isOccluded(const irr::core::vector3df& cam_pos,
const irr::core::vector3df& irr_center,
float radius)
{
if (!m_triangle_mesh)
return false;
float radius_2 = radius * radius;
float distance_2 = (cam_pos - irr_center).getLengthSQ();
// Inside the sphere
if (distance_2 <= radius_2)
return false;
// Calculate points using logarithmic scaling
float distance = sqrtf(distance_2);
// Formula: MAX_POINTS - log2(distance/MIN_DISTANCE) * SCALE_FACTOR
// Clamp between MIN_POINTS and MAX_POINTS
const float MIN_DISTANCE = 80.0f; // Distance at which maximum points are used
const int MAX_POINTS = 12;
const int MIN_POINTS = 3;
const float SCALE_FACTOR = 4.0f; // Controls how quickly the point count decreases
int num_points = MAX_POINTS -
int(std::log2(std::max(distance / MIN_DISTANCE, 1.0f)) * SCALE_FACTOR);
num_points = std::min(MAX_POINTS, std::max(MIN_POINTS, num_points));
btVector3 center = btVector3(irr_center.X, irr_center.Y, irr_center.Z);
PointGenerator* generator = new SpherePointGenerator(center, radius, num_points);
btVector3 test_point;
btVector3 cam_p(cam_pos.X, cam_pos.Y, cam_pos.Z);
bool culled = true;
while (generator->getNextPoint(test_point))
{
btCollisionWorld::ClosestRayResultCallback cb(cam_p, test_point);
cb.m_flags |= btTriangleRaycastCallback::kF_FilterBackfaces;
btTransform from_trans, to_trans;
from_trans.setIdentity();
from_trans.setOrigin(cam_p);
to_trans.setIdentity();
to_trans.setOrigin(test_point);
btCollisionWorld::rayTestSingle(from_trans, to_trans,
m_occluder_object, m_occluder_shape,
m_occluder_object->getWorldTransform(), cb);
if (!cb.hasHit() ||
(cb.m_hitPointWorld - test_point).length2() <= radius_2 || // Hit point inside the sphere
(cb.m_hitPointWorld - cam_p).length2() > (test_point - cam_p).length2()) // Hit point behind the sphere
{
culled = false;
break;
}
}
delete generator;
return culled;
} // isOccluded
}
+112
View File
@@ -0,0 +1,112 @@
#include "ge_spm.hpp"
#include "ge_animation.hpp"
#include "ge_spm_buffer.hpp"
#include <algorithm>
namespace GE
{
GESPM::GESPM()
: m_fps(0.0f), m_bind_frame(0), m_total_joints(0), m_joint_using(0),
m_frame_count(0)
{
} // GESPM
// ----------------------------------------------------------------------------
GESPM::~GESPM()
{
for (unsigned i = 0; i < m_buffer.size(); i++)
m_buffer[i]->drop();
} // ~GESPM
// ----------------------------------------------------------------------------
IMeshBuffer* GESPM::getMeshBuffer(u32 nr) const
{
if (nr < m_buffer.size())
return m_buffer[nr];
else
return NULL;
} // getMeshBuffer
// ----------------------------------------------------------------------------
IMeshBuffer* GESPM::getMeshBuffer(const video::SMaterial &material) const
{
for (unsigned i = 0; i < m_buffer.size(); i++)
{
if (m_buffer[i]->getMaterial() == material)
return m_buffer[i];
}
return NULL;
} // getMeshBuffer
// ----------------------------------------------------------------------------
void GESPM::finalize()
{
m_bounding_box.reset(0.0f, 0.0f, 0.0f);
for (unsigned i = 0; i < m_buffer.size(); i++)
{
m_bounding_box.addInternalBox(m_buffer[i]->getBoundingBox());
m_buffer[i]->createVertexIndexBuffer();
}
for (Armature& arm : getArmatures())
{
arm.getInterpolatedMatrices((float)m_bind_frame);
for (auto& p : arm.m_world_matrices)
{
p.second = false;
}
for (unsigned i = 0; i < arm.m_joint_names.size(); i++)
{
core::matrix4 m;
arm.getWorldMatrix(arm.m_interpolated_matrices, i).getInverse(m);
arm.m_joint_matrices[i] = m;
}
}
} // finalize
// ----------------------------------------------------------------------------
void GESPM::getSkinningMatrices(f32 frame, std::vector<core::matrix4>& dest,
float frame_interpolating, float rate)
{
unsigned accumulated_joints = 0;
for (unsigned i = 0; i < m_all_armatures.size(); i++)
{
m_all_armatures[i].getPose(frame, &dest[accumulated_joints],
frame_interpolating, rate);
accumulated_joints += m_all_armatures[i].m_joint_used;
}
} // getSkinningMatrices
// ----------------------------------------------------------------------------
s32 GESPM::getJointIDWithArm(const c8* name, unsigned* arm_id) const
{
for (unsigned i = 0; i < m_all_armatures.size(); i++)
{
const Armature& arm = m_all_armatures[i];
auto found = std::find(arm.m_joint_names.begin(),
arm.m_joint_names.end(), name);
if (found != arm.m_joint_names.end())
{
if (arm_id != NULL)
{
*arm_id = i;
}
return (int)(found - arm.m_joint_names.begin());
}
}
return -1;
} // getJointIDWithArm
// ----------------------------------------------------------------------------
void GESPM::removeMeshBuffer(u32 nr)
{
if (nr < m_buffer.size())
{
m_buffer[nr]->drop();
m_buffer.erase(m_buffer.begin() + nr);
}
} // removeMeshBuffer
}
+114
View File
@@ -0,0 +1,114 @@
#include "ge_spm_buffer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
#include <stdexcept>
#include "mini_glm.hpp"
namespace GE
{
// ----------------------------------------------------------------------------
void GESPMBuffer::createVertexIndexBuffer()
{
if (GEVulkanFeatures::supportsBaseVertexRendering())
return;
GEVulkanDriver* vk = getVKDriver();
size_t total_pitch = getVertexPitchFromType(video::EVT_SKINNED_MESH);
size_t bone_pitch = sizeof(int16_t) * 8;
size_t static_pitch = total_pitch - bone_pitch;
size_t vbo_size = getVertexCount() * static_pitch;
m_ibo_offset = vbo_size;
size_t ibo_size = getIndexCount() * sizeof(uint16_t);
size_t total_size = vbo_size + ibo_size;
if (m_has_skinning)
{
total_size += getPadding(total_size, 4);
m_skinning_vbo_offset = total_size;
total_size += getVertexCount() * bone_pitch;
}
VkBuffer staging_buffer = VK_NULL_HANDLE;
VmaAllocation staging_memory = VK_NULL_HANDLE;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
staging_buffer_create_info.flags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (!vk->createBuffer(total_size,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_memory))
{
throw std::runtime_error("createVertexIndexBuffer create staging "
"buffer failed");
}
uint8_t* mapped;
if (vmaMapMemory(vk->getVmaAllocator(), staging_memory,
(void**)&mapped) != VK_SUCCESS)
throw std::runtime_error("createVertexIndexBuffer vmaMapMemory failed");
size_t real_size = getVertexCount() * total_pitch;
copyToMappedBuffer((uint32_t*)mapped, this);
uint8_t* loc = mapped + getVertexCount() * static_pitch;
memcpy(loc, m_indices.data(), m_indices.size() * sizeof(uint16_t));
if (m_has_skinning)
{
loc = mapped + m_skinning_vbo_offset;
for (unsigned i = 0; i < real_size; i += total_pitch)
{
uint8_t* vertices = ((uint8_t*)getVertices()) + i +
static_pitch;
memcpy(loc, vertices, bone_pitch);
loc += bone_pitch;
}
}
vmaUnmapMemory(vk->getVmaAllocator(), staging_memory);
vmaFlushAllocation(vk->getVmaAllocator(), staging_memory, 0, total_size);
VmaAllocationCreateInfo local_create_info = {};
local_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
if (!vk->createBuffer(total_size,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT, local_create_info, m_buffer,
m_memory))
throw std::runtime_error("updateCache create buffer failed");
vk->copyBuffer(staging_buffer, m_buffer, total_size);
vmaDestroyBuffer(vk->getVmaAllocator(), staging_buffer, staging_memory);
} // createVertexIndexBuffer
// ----------------------------------------------------------------------------
void GESPMBuffer::destroyVertexIndexBuffer()
{
if (m_buffer == VK_NULL_HANDLE || m_memory == VK_NULL_HANDLE)
return;
getVKDriver()->waitIdle();
vmaDestroyBuffer(getVKDriver()->getVmaAllocator(), m_buffer, m_memory);
m_buffer = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
} // destroyVertexIndexBuffer
// ----------------------------------------------------------------------------
void GESPMBuffer::setNormal(u32 i, const core::vector3df& normal)
{
m_vertices[i].m_normal = MiniGLM::compressVector3(normal);
} // setNormal
// ----------------------------------------------------------------------------
void GESPMBuffer::setTCoords(u32 i, const core::vector2df& tcoords)
{
m_vertices[i].m_all_uvs[0] = MiniGLM::toFloat16(tcoords.X);
m_vertices[i].m_all_uvs[1] = MiniGLM::toFloat16(tcoords.Y);
} // setTCoords
}
+153
View File
@@ -0,0 +1,153 @@
#include "ge_main.hpp"
#include "ge_dx9_texture.hpp"
#include "ge_gl_texture.hpp"
#include "ge_vulkan_texture.hpp"
#include "ge_texture.hpp"
#include <IFileSystem.h>
#include <IVideoDriver.h>
namespace GE
{
using namespace irr;
video::IImage* getResizedImage(const std::string& path,
const core::dimension2du& max_size,
core::dimension2d<u32>* orig_size,
const core::dimension2d<u32>* target_size)
{
io::IReadFile* file =
getDriver()->getFileSystem()->createAndOpenFile(path.c_str());
if (file == NULL)
return NULL;
video::IImage* image = getResizedImage(file, max_size, orig_size,
target_size);
file->drop();
return image;
} // getResizedImage
// ----------------------------------------------------------------------------
core::dimension2du getResizingTarget(const core::dimension2du& orig_size,
const core::dimension2du& max_size)
{
bool has_npot = !getGEConfig()->m_disable_npot_texture &&
getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT);
core::dimension2du tex_size = orig_size.getOptimalSize(!has_npot);
if (tex_size.Width > max_size.Width)
tex_size.Width = max_size.Width;
if (tex_size.Height > max_size.Height)
tex_size.Height = max_size.Height;
return tex_size;
} // getResizingTarget
// ----------------------------------------------------------------------------
video::IImage* getResizedImageFullPath(const io::path& fullpath,
const core::dimension2d<u32>& max_size,
core::dimension2d<u32>* orig_size,
const core::dimension2d<u32>* target_size)
{
io::IReadFile* file = io::createReadFile(fullpath);
if (file == NULL)
return NULL;
video::IImage* texture_image = getResizedImage(file, max_size, orig_size,
target_size);
file->drop();
return texture_image;
} // getResizedImageFullPath
// ----------------------------------------------------------------------------
video::IImage* getResizedImage(irr::io::IReadFile* file,
const core::dimension2du& max_size,
core::dimension2d<u32>* orig_size,
const core::dimension2d<u32>* target_size)
{
video::IImage* image = getDriver()->createImageFromFile(file);
if (image == NULL)
return NULL;
if (orig_size)
*orig_size = image->getDimension();
core::dimension2du img_size = image->getDimension();
core::dimension2du tex_size;
if (target_size)
tex_size = *target_size;
else
tex_size = getResizingTarget(img_size, max_size);
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
tex_size != img_size)
{
video::IImage* new_texture = getDriver()->createImage(
video::ECF_A8R8G8B8, tex_size);
if (tex_size != img_size)
image->copyToScaling(new_texture);
else
image->copyTo(new_texture);
image->drop();
return new_texture;
}
return image;
} // getResizedImage
// ----------------------------------------------------------------------------
irr::video::ITexture* createTexture(const std::string& path,
std::function<void(irr::video::IImage*)> image_mani)
{
switch (GE::getDriver()->getDriverType())
{
case video::EDT_OPENGL:
case video::EDT_OGLES2:
return new GEGLTexture(path, image_mani);
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
case video::EDT_DIRECT3D9:
return new GEDX9Texture(path, image_mani);
#endif
case video::EDT_VULKAN:
return new GEVulkanTexture(path, image_mani);
default:
return NULL;
}
} // createTexture
// ----------------------------------------------------------------------------
irr::video::ITexture* createTexture(video::IImage* img,
const std::string& name)
{
switch (GE::getDriver()->getDriverType())
{
case video::EDT_OPENGL:
case video::EDT_OGLES2:
return new GEGLTexture(img, name);
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
case video::EDT_DIRECT3D9:
return new GEDX9Texture(img, name);
#endif
case video::EDT_VULKAN:
return new GEVulkanTexture(img, name);
default:
return NULL;
}
} // createTexture
// ----------------------------------------------------------------------------
irr::video::ITexture* createFontTexture(const std::string& name,
unsigned size, bool single_channel)
{
switch (GE::getDriver()->getDriverType())
{
case video::EDT_OPENGL:
case video::EDT_OGLES2:
return new GEGLTexture(name, size, single_channel);
#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
case video::EDT_DIRECT3D9:
return new GEDX9Texture(name, size);
#endif
case video::EDT_VULKAN:
return new GEVulkanTexture(name, size, single_channel);
default:
return NULL;
}
} // createFontTexture
}
+2
View File
@@ -0,0 +1,2 @@
#define VMA_IMPLEMENTATION
#include "ge_vma.hpp"
@@ -0,0 +1,453 @@
#include "ge_vulkan_2d_renderer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_dynamic_buffer.hpp"
#include "ge_vulkan_fbo_texture.hpp"
#include "ge_vulkan_features.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include "ge_vulkan_texture_descriptor.hpp"
#include <algorithm>
#include <cstddef>
#include <map>
#include <string>
#include <stdexcept>
#include <utility>
#include "rect.h"
#include "vector2d.h"
#include "SColor.h"
namespace GE
{
// ============================================================================
namespace GEVulkan2dRenderer
{
using namespace irr;
// ============================================================================
GEVulkanDriver* g_vk;
VkPipelineLayout g_pipeline_layout = VK_NULL_HANDLE;
VkPipeline g_graphics_pipeline = VK_NULL_HANDLE;
GEVulkanTextureDescriptor* g_texture_descriptor = NULL;
GEVulkanDynamicBuffer* g_tris_buffer = NULL;
std::vector<VkDescriptorSet> g_descriptor_sets;
struct Tri
{
core::vector2df pos;
video::SColor color;
core::vector2df uv;
int sampler_idx;
};
std::vector<irr::core::recti> g_tris_clip;
std::vector<Tri> g_tris_queue;
std::vector<uint16_t> g_tris_index_queue;
} // GEVulkan2dRenderer
// ============================================================================
void GEVulkan2dRenderer::init(GEVulkanDriver* vk)
{
g_vk = vk;
g_texture_descriptor = new GEVulkanTextureDescriptor(
GEVulkanShaderManager::getSamplerSize(), 1,
GEVulkanFeatures::supportsBindTexturesAtOnce());
g_texture_descriptor->setSamplerUse(GVS_2D_RENDER);
createPipelineLayout();
createGraphicsPipeline();
createTrisBuffers();
} // init
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::destroy()
{
delete g_tris_buffer;
g_tris_buffer = NULL;
if (!g_vk)
return;
delete g_texture_descriptor;
g_texture_descriptor = NULL;
vkDestroyPipeline(g_vk->getDevice(), g_graphics_pipeline, NULL);
vkDestroyPipelineLayout(g_vk->getDevice(), g_pipeline_layout, NULL);
} // destroy
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createPipelineLayout()
{
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 1;
pipeline_layout_info.pSetLayouts = g_texture_descriptor->getDescriptorSetLayout();
VkResult result = vkCreatePipelineLayout(g_vk->getDevice(), &pipeline_layout_info,
nullptr, &g_pipeline_layout);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreatePipelineLayout failed");
} // createPipelineLayout
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createGraphicsPipeline()
{
VkPipelineShaderStageCreateInfo vert_shader_stage_info = {};
vert_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vert_shader_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
vert_shader_stage_info.module = GEVulkanShaderManager::getShader("2d_render.vert");
vert_shader_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo frag_shader_stage_info = {};
frag_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
frag_shader_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
frag_shader_stage_info.module = GEVulkanShaderManager::getShader("2d_render.frag");
frag_shader_stage_info.pName = "main";
VkPipelineShaderStageCreateInfo shader_stages[] =
{
vert_shader_stage_info,
frag_shader_stage_info
};
VkVertexInputBindingDescription binding_description = {};
binding_description.binding = 0;
binding_description.stride = sizeof(Tri);
binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
std::array<VkVertexInputAttributeDescription, 4> attribute_descriptions = {};
attribute_descriptions[0].binding = 0;
attribute_descriptions[0].location = 0;
attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[0].offset = offsetof(Tri, pos);
attribute_descriptions[1].binding = 0;
attribute_descriptions[1].location = 1;
attribute_descriptions[1].format = VK_FORMAT_R8G8B8A8_UNORM;
attribute_descriptions[1].offset = offsetof(Tri, color);
attribute_descriptions[2].binding = 0;
attribute_descriptions[2].location = 2;
attribute_descriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
attribute_descriptions[2].offset = offsetof(Tri, uv);
attribute_descriptions[3].binding = 0;
attribute_descriptions[3].location = 3;
attribute_descriptions[3].format = VK_FORMAT_R32_SINT;
attribute_descriptions[3].offset = offsetof(Tri, sampler_idx);
VkPipelineVertexInputStateCreateInfo vertex_input_info = {};
vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount = 1;
vertex_input_info.vertexAttributeDescriptionCount = (uint32_t)(attribute_descriptions.size());
vertex_input_info.pVertexBindingDescriptions = &binding_description;
vertex_input_info.pVertexAttributeDescriptions = &attribute_descriptions[0];
VkPipelineInputAssemblyStateCreateInfo input_assembly = {};
input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
input_assembly.primitiveRestartEnable = VK_FALSE;
VkViewport viewport = {};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)g_vk->getSwapChainExtent().width;
viewport.height = (float)g_vk->getSwapChainExtent().height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = g_vk->getSwapChainExtent();
VkPipelineViewportStateCreateInfo viewport_state = {};
viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state.viewportCount = 1;
viewport_state.pViewports = &viewport;
viewport_state.scissorCount = 1;
viewport_state.pScissors = &scissor;
VkPipelineRasterizationStateCreateInfo rasterizer = {};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
VkPipelineMultisampleStateCreateInfo multisampling = {};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineDepthStencilStateCreateInfo depth_stencil = {};
depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depth_stencil.depthTestEnable = VK_FALSE;
depth_stencil.depthWriteEnable = VK_FALSE;
depth_stencil.depthBoundsTestEnable = VK_FALSE;
depth_stencil.stencilTestEnable = VK_FALSE;
VkPipelineColorBlendAttachmentState color_blend_attachment = {};
color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT;
color_blend_attachment.blendEnable = VK_TRUE;
color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo color_blending = {};
color_blending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
color_blending.logicOpEnable = VK_FALSE;
color_blending.logicOp = VK_LOGIC_OP_COPY;
color_blending.attachmentCount = 1;
color_blending.pAttachments = &color_blend_attachment;
color_blending.blendConstants[0] = 0.0f;
color_blending.blendConstants[1] = 0.0f;
color_blending.blendConstants[2] = 0.0f;
color_blending.blendConstants[3] = 0.0f;
std::array<VkDynamicState, 2> dynamic_state =
{
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_VIEWPORT
};
VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
dynamic_state_info.dynamicStateCount = dynamic_state.size(),
dynamic_state_info.pDynamicStates = dynamic_state.data();
VkGraphicsPipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = 2;
pipeline_info.pStages = shader_stages;
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly;
pipeline_info.pViewportState = &viewport_state;
pipeline_info.pRasterizationState = &rasterizer;
pipeline_info.pMultisampleState = &multisampling;
pipeline_info.pDepthStencilState = &depth_stencil;
pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDynamicState = &dynamic_state_info;
pipeline_info.layout = g_pipeline_layout;
GEVulkanFBOTexture* rtt = g_vk->getRTTTexture();
bool rpc1 = rtt && rtt->getRTTRenderPassCount() == 1;
bool sco = rtt && rtt->useSwapChainOutput();
if (sco)
{
if (rpc1)
pipeline_info.renderPass = rtt->getRTTRenderPass();
else
pipeline_info.renderPass = rtt->getRTTRenderPass(rtt->getRTTRenderPassCount() - 1);
}
else
pipeline_info.renderPass = g_vk->getRenderPass();
pipeline_info.subpass = sco && rpc1 ? 2 : 0;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
VkResult result = vkCreateGraphicsPipelines(g_vk->getDevice(),
VK_NULL_HANDLE, 1, &pipeline_info, NULL, &g_graphics_pipeline);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateGraphicsPipelines failed");
} // createGraphicsPipeline
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createTrisBuffers()
{
g_tris_buffer = new GEVulkanDynamicBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
12000, GEVulkanDriver::getMaxFrameInFlight(), 0);
} // createTrisBuffers
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::uploadTrisBuffers()
{
if (g_tris_queue.empty())
return;
g_tris_buffer->setCurrentData(
{
{ (void*)g_tris_queue.data(), g_tris_queue.size() * sizeof(Tri) },
{ (void*)g_tris_index_queue.data(), g_tris_index_queue.size() * sizeof(uint16_t) }
});
g_texture_descriptor->updateDescriptor();
} // uploadTrisBuffers
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::handleDeletedTextures()
{
g_texture_descriptor->handleDeletedTextures();
} // handleDeletedTextures
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::render()
{
if (g_tris_queue.empty())
return;
VkDeviceSize offsets[] = {0};
VkBuffer buffer = VK_NULL_HANDLE;
unsigned idx = 0;
unsigned idx_count = 0;
int sampler_idx = 0;
core::recti clip;
const VkDescriptorSet* descriptor_set =
g_texture_descriptor->getDescriptorSet();
buffer = g_tris_buffer->getCurrentBuffer();
if (buffer == VK_NULL_HANDLE)
goto end;
vkCmdBindPipeline(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_graphics_pipeline);
vkCmdBindVertexBuffers(g_vk->getCurrentCommandBuffer(), 0, 1,
&buffer, offsets);
vkCmdBindIndexBuffer(g_vk->getCurrentCommandBuffer(), buffer,
g_tris_queue.size() * sizeof(Tri), VK_INDEX_TYPE_UINT16);
VkViewport vp;
vp.x = g_vk->getViewPort().UpperLeftCorner.X;
vp.y = g_vk->getViewPort().UpperLeftCorner.Y;
vp.width = g_vk->getViewPort().getWidth();
vp.height = g_vk->getViewPort().getHeight();
vp.minDepth = 0;
vp.maxDepth = 1.0f;
g_vk->getRotatedViewport(&vp, false/*handle_rtt*/);
vkCmdSetViewport(g_vk->getCurrentCommandBuffer(), 0, 1, &vp);
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
{
vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1,
descriptor_set, 0, NULL);
}
sampler_idx = g_tris_queue[0].sampler_idx;
clip = g_tris_clip[0];
for (; idx < g_tris_index_queue.size(); idx += 3)
{
Tri& cur_tri = g_tris_queue[g_tris_index_queue[idx]];
int cur_sampler_idx = cur_tri.sampler_idx;
if (GEVulkanFeatures::supportsDifferentTexturePerDraw())
cur_sampler_idx = g_tris_queue[0].sampler_idx;
const core::recti& cur_clip = g_tris_clip[g_tris_index_queue[idx]];
if (cur_sampler_idx != sampler_idx || cur_clip != clip)
{
if (!GEVulkanFeatures::supportsBindTexturesAtOnce())
{
vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1,
&descriptor_set[sampler_idx], 0, NULL);
}
VkRect2D scissor;
scissor.offset.x = clip.UpperLeftCorner.X;
scissor.offset.y = clip.UpperLeftCorner.Y;
scissor.extent.width = clip.getWidth();
scissor.extent.height = clip.getHeight();
g_vk->getRotatedRect2D(&scissor);
vkCmdSetScissor(g_vk->getCurrentCommandBuffer(), 0, 1, &scissor);
vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1,
idx - idx_count, 0, 0);
sampler_idx = cur_sampler_idx;
clip = cur_clip;
idx_count = 3;
}
else
{
idx_count += 3;
}
}
if (!GEVulkanFeatures::supportsBindTexturesAtOnce())
{
vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1,
&descriptor_set[sampler_idx], 0, NULL);
}
VkRect2D scissor;
scissor.offset.x = clip.UpperLeftCorner.X;
scissor.offset.y = clip.UpperLeftCorner.Y;
scissor.extent.width = clip.getWidth();
scissor.extent.height = clip.getHeight();
g_vk->getRotatedRect2D(&scissor);
vkCmdSetScissor(g_vk->getCurrentCommandBuffer(), 0, 1, &scissor);
vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(), idx_count, 1,
idx - idx_count, 0, 0);
end:
clear();
} // render
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::clear()
{
g_tris_queue.clear();
g_tris_index_queue.clear();
g_tris_clip.clear();
} // clear
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices,
unsigned vertices_count,
uint16_t* indices,
unsigned indices_count,
const irr::video::ITexture* t)
{
uint16_t last_index = (uint16_t)g_tris_queue.size();
if (last_index + vertices_count > 65535)
return;
int sampler_idx = g_texture_descriptor->getTextureID(&t);
for (unsigned idx = 0; idx < vertices_count; idx++)
{
Tri t;
const S3DVertex& vertex = vertices[idx];
t.pos = core::vector2df(
vertex.Pos.X / g_vk->getCurrentRenderTargetSize().Width,
vertex.Pos.Y / g_vk->getCurrentRenderTargetSize().Height);
t.pos = t.pos * 2.0f;
t.pos -= 1.0f;
core::vector3df position = core::vector3df(t.pos.X, t.pos.Y, 0);
g_vk->getPreRotationMatrix().transformVect(position);
t.pos = core::vector2df(position.X, position.Y);
t.color = vertex.Color;
t.uv = vertex.TCoords;
t.sampler_idx = sampler_idx;
g_tris_queue.push_back(t);
g_tris_clip.push_back(g_vk->getCurrentClip());
}
const core::recti& fclip = g_vk->getFullscreenClip();
for (unsigned idx = 0; idx < indices_count * 3; idx += 3)
{
g_tris_index_queue.push_back(last_index + indices[idx]);
g_tris_index_queue.push_back(last_index + indices[idx + 1]);
g_tris_index_queue.push_back(last_index + indices[idx + 2]);
const core::recti& cur_clip = g_tris_clip[last_index + indices[idx]];
const core::vector3df& pos_1 = vertices[indices[idx]].Pos;
const core::vector3df& pos_2 = vertices[indices[idx + 1]].Pos;
const core::vector3df& pos_3 = vertices[indices[idx + 2]].Pos;
if (GEVulkanFeatures::supportsDifferentTexturePerDraw() &&
fclip != cur_clip &&
cur_clip.isPointInside(core::position2di(pos_1.X, pos_1.Y)) &&
cur_clip.isPointInside(core::position2di(pos_2.X, pos_2.Y)) &&
cur_clip.isPointInside(core::position2di(pos_3.X, pos_3.Y)))
{
g_tris_clip[last_index + indices[idx]] = fclip;
g_tris_clip[last_index + indices[idx + 1]] = fclip;
g_tris_clip[last_index + indices[idx + 2]] = fclip;
}
}
} // addVerticesIndices
}
@@ -0,0 +1,41 @@
#ifndef HEADER_GE_VULKAN_2D_RENDERER_HPP
#define HEADER_GE_VULKAN_2D_RENDERER_HPP
#include "vulkan_wrapper.h"
#include "ITexture.h"
#include "S3DVertex.h"
namespace GE
{
class GEVulkanDriver;
namespace GEVulkan2dRenderer
{
// ----------------------------------------------------------------------------
void init(GEVulkanDriver*);
// ----------------------------------------------------------------------------
void destroy();
// ----------------------------------------------------------------------------
void createPipelineLayout();
// ----------------------------------------------------------------------------
void createGraphicsPipeline();
// ----------------------------------------------------------------------------
void createTrisBuffers();
// ----------------------------------------------------------------------------
void uploadTrisBuffers();
// ----------------------------------------------------------------------------
void handleDeletedTextures();
// ----------------------------------------------------------------------------
void render();
// ----------------------------------------------------------------------------
void clear();
// ----------------------------------------------------------------------------
void addVerticesIndices(irr::video::S3DVertex* vertices,
unsigned vertices_count, uint16_t* indices,
unsigned indices_count,
const irr::video::ITexture* t);
}; // GEVulkanRenderer
}
#endif
@@ -0,0 +1,135 @@
#include "ge_vulkan_animated_mesh_scene_node.hpp"
#include "ge_animation.hpp"
#include "ge_spm.hpp"
#include "ISceneManager.h"
#include "../../../lib/irrlicht/source/Irrlicht/CBoneSceneNode.h"
#include <limits>
namespace GE
{
GEVulkanAnimatedMeshSceneNode::GEVulkanAnimatedMeshSceneNode(irr::scene::IAnimatedMesh* mesh,
irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
const irr::core::vector3df& position,
const irr::core::vector3df& rotation,
const irr::core::vector3df& scale)
: irr::scene::CAnimatedMeshSceneNode(mesh, parent, mgr, id, position,
rotation, scale)
{
m_saved_transition_frame = -1.0f;
} // GEVulkanAnimatedMeshSceneNode
// ----------------------------------------------------------------------------
GESPM* GEVulkanAnimatedMeshSceneNode::getSPM() const
{
return static_cast<GESPM*>(Mesh);
} // getSPM
// ----------------------------------------------------------------------------
void GEVulkanAnimatedMeshSceneNode::OnRegisterSceneNode()
{
if (!IsVisible)
return;
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
ISceneNode::OnRegisterSceneNode();
} // OnRegisterSceneNode
// ----------------------------------------------------------------------------
void GEVulkanAnimatedMeshSceneNode::setMesh(irr::scene::IAnimatedMesh* mesh)
{
CAnimatedMeshSceneNode::setMesh(mesh);
cleanJoints();
GESPM* spm = getSPM();
if (!spm || spm->isStatic())
return;
unsigned bone_idx = 0;
m_skinning_matrices.resize(spm->getJointCount());
for (Armature& arm : spm->getArmatures())
{
for (const std::string& bone_name : arm.m_joint_names)
{
m_joint_nodes[bone_name] = new CBoneSceneNode(this,
SceneManager, 0, bone_idx++, bone_name.c_str());
m_joint_nodes.at(bone_name)->drop();
m_joint_nodes.at(bone_name)->setSkinningSpace(EBSS_GLOBAL);
}
}
} // setMesh
// ----------------------------------------------------------------------------
void GEVulkanAnimatedMeshSceneNode::OnAnimate(irr::u32 time_ms)
{
GESPM* spm = getSPM();
if (!spm || spm->isStatic())
{
IAnimatedMeshSceneNode::OnAnimate(time_ms);
return;
}
// first frame
if (LastTimeMs == 0)
LastTimeMs = time_ms;
// set CurrentFrameNr
buildFrameNr(time_ms - LastTimeMs);
LastTimeMs = time_ms;
spm->getSkinningMatrices(getFrameNr(), m_skinning_matrices,
m_saved_transition_frame, TransitingBlend);
recursiveUpdateAbsolutePosition();
for (Armature& arm : spm->getArmatures())
{
for (unsigned i = 0; i < arm.m_joint_names.size(); i++)
{
m_joint_nodes.at(arm.m_joint_names[i])->setAbsoluteTransformation
(AbsoluteTransformation * arm.m_world_matrices[i].first);
}
}
IAnimatedMeshSceneNode::OnAnimate(time_ms);
} // OnAnimate
// ----------------------------------------------------------------------------
irr::scene::IBoneSceneNode* GEVulkanAnimatedMeshSceneNode::getJointNode(const irr::c8* joint_name)
{
auto ret = m_joint_nodes.find(joint_name);
if (ret != m_joint_nodes.end())
return ret->second;
return NULL;
} // getJointNode
// ----------------------------------------------------------------------------
irr::scene::IBoneSceneNode* GEVulkanAnimatedMeshSceneNode::getJointNode(irr::u32 joint_id)
{
irr::u32 idx = 0;
for (auto& p : m_joint_nodes)
{
if (joint_id == idx)
return p.second;
idx++;
}
return NULL;
} // getJointNode
// ----------------------------------------------------------------------------
void GEVulkanAnimatedMeshSceneNode::setTransitionTime(irr::f32 Time)
{
if (Time == 0.0f)
{
TransitingBlend = TransitionTime = Transiting = 0;
m_saved_transition_frame = -1.0;
}
else
{
const u32 ttime = (u32)core::floor32(Time * 1000.0f);
TransitionTime = ttime;
Transiting = core::reciprocal((f32)TransitionTime);
TransitingBlend = 0.0f;
m_saved_transition_frame = getFrameNr();
}
} // setTransitionTime
}
@@ -0,0 +1,64 @@
#ifndef HEADER_GE_VULKAN_ANIMATED_MESH_SCENE_NODE_HPP
#define HEADER_GE_VULKAN_ANIMATED_MESH_SCENE_NODE_HPP
#include "../source/Irrlicht/CAnimatedMeshSceneNode.h"
#include <cassert>
#include <string>
#include <vector>
#include <unordered_map>
namespace GE
{
class GESPM;
class GEVulkanAnimatedMeshSceneNode : public irr::scene::CAnimatedMeshSceneNode
{
private:
std::unordered_map<std::string, irr::scene::IBoneSceneNode*> m_joint_nodes;
float m_saved_transition_frame;
std::vector<irr::core::matrix4> m_skinning_matrices;
// ------------------------------------------------------------------------
void cleanJoints()
{
for (auto& p : m_joint_nodes)
removeChild(p.second);
m_joint_nodes.clear();
m_skinning_matrices.clear();
}
public:
// ------------------------------------------------------------------------
GEVulkanAnimatedMeshSceneNode(irr::scene::IAnimatedMesh* mesh,
irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0),
const irr::core::vector3df& rotation = irr::core::vector3df(0, 0, 0),
const irr::core::vector3df& scale = irr::core::vector3df(1.0f, 1.0f, 1.0f));
// ------------------------------------------------------------------------
~GEVulkanAnimatedMeshSceneNode() { cleanJoints(); }
// ------------------------------------------------------------------------
virtual void setMesh(irr::scene::IAnimatedMesh* mesh);
// ------------------------------------------------------------------------
virtual void OnAnimate(irr::u32 time_ms);
// ------------------------------------------------------------------------
virtual irr::scene::IBoneSceneNode* getJointNode(const irr::c8* joint_name);
// ------------------------------------------------------------------------
virtual irr::scene::IBoneSceneNode* getJointNode(irr::u32 joint_id);
// ------------------------------------------------------------------------
virtual irr::u32 getJointCount() const { return m_joint_nodes.size(); }
// ------------------------------------------------------------------------
virtual void setTransitionTime(irr::f32 Time);
// ------------------------------------------------------------------------
GESPM* getSPM() const;
// ------------------------------------------------------------------------
virtual void OnRegisterSceneNode();
// ------------------------------------------------------------------------
const std::vector<irr::core::matrix4>& getSkinningMatrices() const
{ return m_skinning_matrices; }
}; // GEVulkanAnimatedMeshSceneNode
}
#endif
@@ -0,0 +1,355 @@
#include "ge_vulkan_array_texture.hpp"
#include "ge_main.hpp"
#include "ge_mipmap_generator.hpp"
#include "ge_compressor_astc_4x4.hpp"
#include "ge_compressor_bptc_bc7.hpp"
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <IImageLoader.h>
#include <cassert>
#include <stdexcept>
namespace GE
{
// ============================================================================
GEVulkanArrayTexture::ThreadLoader::ThreadLoader(GEVulkanArrayTexture* texture,
const std::vector<io::path>& list,
std::function<void(video::IImage*, unsigned)> image_mani,
video::SColor unicolor,
VkImageLayout first_layout)
: m_texture(texture), m_list(list),
m_image_mani(image_mani), m_unicolor(unicolor),
m_first_layout(first_layout)
{
m_images.resize(m_texture->m_layer_count);
m_mipmaps.resize(m_texture->m_layer_count);
m_texture->m_image_view_lock.lock();
m_texture->m_thread_loading_lock.lock();
} // GEVulkanArrayTexture::ThreadLoader
// ----------------------------------------------------------------------------
GEVulkanArrayTexture::ThreadLoader::~ThreadLoader()
{
bool storage_image = m_list[0].empty();
VkDeviceSize image_size = 0;
switch (m_texture->m_internal_format)
{
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_BC7_UNORM_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
image_size = get4x4CompressedTextureSize(m_texture->m_size.Width,
m_texture->m_size.Height);
break;
default:
image_size = m_texture->m_size.Width * m_texture->m_size.Height * 4;
break;
}
VkDeviceSize mipmap_data_size = m_mipmaps[0]->getMipmapSizes();
VkDeviceSize image_total_size = image_size + mipmap_data_size;
image_total_size *= m_mipmaps.size();
VkBuffer staging_buffer = VK_NULL_HANDLE;
VmaAllocation staging_buffer_allocation = NULL;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
staging_buffer_create_info.flags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
staging_buffer_create_info.preferredFlags =
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
uint8_t* mapped;
unsigned offset = 0;
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
GEVulkanDriver* vk = m_texture->m_vk;
VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
if (storage_image)
usage_flags |= VK_IMAGE_USAGE_STORAGE_BIT;
if (!vk->createBuffer(image_total_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation))
{
goto destroy;
}
if (vmaMapMemory(vk->getVmaAllocator(),
staging_buffer_allocation, (void**)&mapped) != VK_SUCCESS)
{
goto destroy;
}
for (unsigned i = 0; i < m_mipmaps.size(); i++)
{
for (GEImageLevel& level : m_mipmaps[i]->getAllLevels())
{
memcpy(mapped, level.m_data, level.m_size);
mapped += level.m_size;
}
}
vmaUnmapMemory(vk->getVmaAllocator(), staging_buffer_allocation);
vmaFlushAllocation(vk->getVmaAllocator(), staging_buffer_allocation, 0,
image_total_size);
if (!m_texture->createImage(usage_flags))
goto destroy;
command_buffer = GEVulkanCommandLoader::beginSingleTimeCommands();
m_texture->transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
for (unsigned i = 0; i < m_mipmaps.size(); i++)
{
std::vector<GEImageLevel>& levels = m_mipmaps[i]->getAllLevels();
for (unsigned j = 0; j < levels.size(); j++)
{
GEImageLevel& level = levels[j];
m_texture->copyBufferToImage(command_buffer, staging_buffer,
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, j, i);
offset += level.m_size;
}
}
if (m_first_layout == VK_IMAGE_LAYOUT_UNDEFINED)
{
if (storage_image)
m_first_layout = VK_IMAGE_LAYOUT_GENERAL;
else
m_first_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
m_texture->transitionImageLayout(command_buffer,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_first_layout);
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
m_texture->createImageView(VK_IMAGE_ASPECT_COLOR_BIT, !storage_image);
destroy:
m_texture->m_image_view_lock.unlock();
for (video::IImage* image : m_images)
image->drop();
for (GEMipmapGenerator* mipmap_generator : m_mipmaps)
delete mipmap_generator;
if (staging_buffer != VK_NULL_HANDLE)
{
vmaDestroyBuffer(vk->getVmaAllocator(), staging_buffer,
staging_buffer_allocation);
}
m_texture->m_thread_loading_lock.unlock();
} // ~GEVulkanArrayTexture::ThreadLoader
// ----------------------------------------------------------------------------
void GEVulkanArrayTexture::ThreadLoader::load(unsigned layer)
{
const io::path& fullpath = m_list[layer];
core::dimension2du size = m_texture->m_size;
video::IImage* texture_image;
if (fullpath.empty())
{
std::vector<video::SColor> data(size.Width * size.Height, m_unicolor);
texture_image = m_texture->m_vk->createImageFromData(
video::ECF_A8R8G8B8, size, data.data(), false/*ownForeignMemory*/);
}
else
texture_image = getResizedImageFullPath(fullpath, size, NULL, &size);
if (texture_image == NULL)
{
throw std::runtime_error(
"Missing texture_image in "
"GEVulkanArrayTexture::ThreadLoader::load");
}
if (m_image_mani)
m_image_mani(texture_image, layer);
uint8_t* texture_data = (uint8_t*)texture_image->lock();
m_texture->bgraConversion(texture_data);
GEMipmapGenerator* mipmap_generator = NULL;
const bool normal_map = (std::string(fullpath.c_str()).find(
"_Normal.") != std::string::npos);
bool texture_compression = getGEConfig()->m_texture_compression &&
!fullpath.empty();
if (texture_compression && GEVulkanFeatures::supportsASTC4x4())
{
if (layer == 0)
m_texture->m_internal_format = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
mipmap_generator = new GECompressorASTC4x4(texture_data, 4, size,
normal_map);
}
else if (texture_compression && GEVulkanFeatures::supportsBPTCBC7())
{
if (layer == 0)
m_texture->m_internal_format = VK_FORMAT_BC7_UNORM_BLOCK;
mipmap_generator = new GECompressorBPTCBC7(texture_data, 4, size,
normal_map);
}
else if (texture_compression && GEVulkanFeatures::supportsS3TCBC3())
{
if (layer == 0)
m_texture->m_internal_format = VK_FORMAT_BC3_UNORM_BLOCK;
mipmap_generator = new GECompressorS3TCBC3(texture_data, 4, size,
normal_map);
}
else
{
if (layer == 0)
m_texture->m_internal_format = VK_FORMAT_R8G8B8A8_UNORM;
mipmap_generator = new GEMipmapGenerator(texture_data, 4, size,
normal_map);
}
m_mipmaps[layer] = mipmap_generator;
m_images[layer] = texture_image;
} // GEVulkanArrayTexture::load
// ============================================================================
std::vector<io::path> getPathList(const std::vector<GEVulkanTexture*>& tlist)
{
std::vector<io::path> list;
for (GEVulkanTexture* tex : tlist)
list.push_back(tex->getFullPath());
return list;
} // getPathList
// ============================================================================
GEVulkanArrayTexture::GEVulkanArrayTexture(const std::vector<io::path>& list,
VkImageViewType type,
std::function<void(video::IImage*,
unsigned)> image_mani)
: GEVulkanTexture()
{
if (list.empty())
throw std::runtime_error("empty texture list for array texture");
m_layer_count = list.size();
m_image_view_type = type;
m_vk = getVKDriver();
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_has_mipmaps = true;
m_locked_data = NULL;
m_internal_format = VK_FORMAT_R8G8B8A8_UNORM;
unsigned width = 0;
unsigned height = 0;
for (unsigned i = 0; i < list.size(); i++)
{
const io::path& fullpath = list[i];
video::IImageLoader* loader = NULL;
io::IReadFile* file = io::createReadFile(fullpath);
if (!file)
{
printf("Missing file %s in GEVulkanArrayTexture, layer %d",
fullpath.c_str(), i);
return;
}
m_vk->createImageFromFile(file, &loader);
core::dimension2du dim;
if (!loader || !loader->getImageSize(file, &dim))
{
file->drop();
printf("Missing image loader for %s in "
"GEVulkanArrayTexture, layer %d", fullpath.c_str(), i);
return;
}
file->drop();
if (m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE ||
m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
{
width = std::max({ width, dim.Width, dim.Height });
height = width;
}
else
{
width = std::max(width, dim.Width);
height = std::max(height, dim.Height);
}
}
const core::dimension2du& max_size = m_vk->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
width = std::min(width, max_size.Width);
height = std::min(height, max_size.Height);
m_size = core::dimension2du(width, height);
m_orig_size = m_size;
std::shared_ptr<ThreadLoader> tl = std::make_shared<ThreadLoader>(this,
list, image_mani);
for (unsigned i = 0; i < list.size(); i++)
{
GEVulkanCommandLoader::addMultiThreadingCommand(
[tl, i](){ tl->load(i); });
}
} // GEVulkanArrayTexture
// ----------------------------------------------------------------------------
GEVulkanArrayTexture::GEVulkanArrayTexture(
const std::vector<GEVulkanTexture*>& textures,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani)
: GEVulkanArrayTexture(getPathList(textures), type,
image_mani)
{
} // GEVulkanArrayTexture
// ----------------------------------------------------------------------------
GEVulkanArrayTexture::GEVulkanArrayTexture(VkFormat internal_format,
VkImageViewType type,
const core::dimension2du& size,
unsigned layer_count,
video::SColor unicolor,
VkImageLayout first_layout)
{
m_layer_count = layer_count;
m_image_view_type = type;
m_vk = getVKDriver();
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_has_mipmaps = true;
m_locked_data = NULL;
m_internal_format = internal_format;
if (size.Width < 4 || size.Height < 4)
throw std::runtime_error("Minimum width and height of 4 is required.");
m_orig_size = m_size = size;
if (m_internal_format == VK_FORMAT_R8G8B8A8_UNORM)
{
std::vector<io::path> list;
for (unsigned i = 0; i < m_layer_count; i++)
list.push_back("");
std::shared_ptr<ThreadLoader> tl = std::make_shared<ThreadLoader>(this,
list, nullptr, unicolor, first_layout);
for (unsigned i = 0; i < list.size(); i++)
{
GEVulkanCommandLoader::addMultiThreadingCommand(
[tl, i](){ tl->load(i); });
}
}
else
{
if (!createImage(VK_IMAGE_USAGE_STORAGE_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT))
{
throw std::runtime_error(
"createImage failed in storage array texture creation");
}
VkCommandBuffer command_buffer =
GEVulkanCommandLoader::beginSingleTimeCommands();
if (first_layout != VK_IMAGE_LAYOUT_UNDEFINED)
{
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
first_layout);
}
else
{
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_GENERAL);
}
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
createImageView(VK_IMAGE_ASPECT_COLOR_BIT);
}
} // GEVulkanArrayTexture
}
@@ -0,0 +1,89 @@
#ifndef HEADER_GE_VULKAN_ARRAY_TEXTURE_HPP
#define HEADER_GE_VULKAN_ARRAY_TEXTURE_HPP
#include "ge_vulkan_texture.hpp"
namespace GE
{
class GEMipmapGenerator;
class GEVulkanDriver;
class GEVulkanArrayTexture : public GEVulkanTexture
{
private:
class ThreadLoader
{
private:
GEVulkanArrayTexture* m_texture;
std::vector<video::IImage*> m_images;
std::vector<GEMipmapGenerator*> m_mipmaps;
std::vector<io::path> m_list;
core::dimension2du m_max_size;
std::function<void(video::IImage*, unsigned)> m_image_mani;
video::SColor m_unicolor;
VkImageLayout m_first_layout;
public:
// --------------------------------------------------------------------
ThreadLoader(GEVulkanArrayTexture* texture,
const std::vector<io::path>& list,
std::function<void(video::IImage*, unsigned)> image_mani,
video::SColor unicolor = video::SColor(),
VkImageLayout first_layout = VK_IMAGE_LAYOUT_UNDEFINED);
// --------------------------------------------------------------------
~ThreadLoader();
// --------------------------------------------------------------------
void load(unsigned layer);
};
public:
// ------------------------------------------------------------------------
GEVulkanArrayTexture(const std::vector<io::path>& full_path_list,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani = nullptr);
// ------------------------------------------------------------------------
GEVulkanArrayTexture(const std::vector<GEVulkanTexture*>& textures,
VkImageViewType type,
std::function<void(video::IImage*, unsigned)>
image_mani = nullptr);
// ------------------------------------------------------------------------
GEVulkanArrayTexture(VkFormat internal_format,
VkImageViewType type, const core::dimension2du& size,
unsigned layer_count, video::SColor unicolor,
VkImageLayout first_layout = VK_IMAGE_LAYOUT_UNDEFINED);
// ------------------------------------------------------------------------
virtual ~GEVulkanArrayTexture() {}
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return video::EDT_VULKAN; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return false; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual void reload() {}
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
}; // GEVulkanArrayTexture
}
#endif
@@ -0,0 +1,59 @@
#include <stdexcept>
#include "ge_vulkan_attachment_texture.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include <stdexcept>
namespace GE
{
GEVulkanAttachmentTexture::GEVulkanAttachmentTexture(GEVulkanDriver* vk,
const core::dimension2d<u32>& size,
VkFormat format,
VkImageUsageFlags iu,
VkImageAspectFlags ia)
: GEVulkanTexture()
{
m_vk = vk;
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_has_mipmaps = false;
m_locked_data = NULL;
m_size = m_orig_size = size;
m_internal_format = format;
if (!createImage(iu))
throw std::runtime_error("createImage failed for attachment texture");
if (!createImageView(ia))
throw std::runtime_error("createImageView failed for attachment texture");
} // GEVulkanAttachmentTexture
// ----------------------------------------------------------------------------
GEVulkanAttachmentTexture* GEVulkanAttachmentTexture::createDepthTexture(
GEVulkanDriver* vk, const core::dimension2d<u32>& size,
bool lazy_allocation)
{
std::vector<VkFormat> preferred =
{
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM
};
VkFormat format = vk->findSupportedFormat(preferred,
VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT);
VkImageUsageFlags iu = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
if (lazy_allocation)
iu |= VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
else
iu |= VK_IMAGE_USAGE_SAMPLED_BIT;
return new GEVulkanAttachmentTexture(vk, size, format, iu,
VK_IMAGE_ASPECT_DEPTH_BIT);
} // createDepthTexture
}
@@ -0,0 +1,63 @@
#ifndef HEADER_GE_VULKAN_ATTACHMENT_TEXTURE_HPP
#define HEADER_GE_VULKAN_ATTACHMENT_TEXTURE_HPP
#include "ge_vulkan_texture.hpp"
namespace GE
{
class GEVulkanDriver;
class GEVulkanAttachmentTexture : public GEVulkanTexture
{
public:
// ------------------------------------------------------------------------
GEVulkanAttachmentTexture(GEVulkanDriver* vk,
const core::dimension2d<u32>& size,
VkFormat format, VkImageUsageFlags iu,
VkImageAspectFlags ia);
// ------------------------------------------------------------------------
virtual ~GEVulkanAttachmentTexture() {}
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_orig_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return video::EDT_VULKAN; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return false; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{ return (u64)(m_image_view.get()->load()); }
// ------------------------------------------------------------------------
virtual void reload() {}
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
// ------------------------------------------------------------------------
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView(
bool srgb = false) const
{ return m_image_view; }
// ------------------------------------------------------------------------
static GEVulkanAttachmentTexture* createDepthTexture(
GEVulkanDriver* vk, const core::dimension2d<u32>& size,
bool lazy_allocation = true);
}; // GEVulkanAttachmentTexture
}
#endif
@@ -0,0 +1,17 @@
#include "ge_vulkan_billboard_buffer.hpp"
#include "ge_main.hpp"
#include "ge_spm.hpp"
#include "ge_vulkan_driver.hpp"
namespace GE
{
GEVulkanBillboardBuffer::GEVulkanBillboardBuffer(
irr::video::SMaterial& billboard_material)
{
m_billboard_buffer = static_cast<GESPMBuffer*>
(getVKDriver()->getBillboardQuad()->getMeshBuffer(0));
m_material = billboard_material;
} // GEVulkanBillboardBuffer
}
@@ -0,0 +1,31 @@
#ifndef HEADER_GE_VULKAN_BILLBOARD_BUFFER_HPP
#define HEADER_GE_VULKAN_BILLBOARD_BUFFER_HPP
#include "ge_spm_buffer.hpp"
namespace GE
{
class GEVulkanBillboardBuffer : public GESPMBuffer
{
private:
GESPMBuffer* m_billboard_buffer;
public:
// ------------------------------------------------------------------------
GEVulkanBillboardBuffer(irr::video::SMaterial& billboard_material);
// ------------------------------------------------------------------------
virtual irr::u32 getIndexCount() const
{ return m_billboard_buffer->getIndexCount(); }
// ------------------------------------------------------------------------
virtual size_t getVBOOffset() const
{ return m_billboard_buffer->getVBOOffset(); }
// ------------------------------------------------------------------------
virtual size_t getIBOOffset() const
{ return m_billboard_buffer->getIBOOffset(); }
// ------------------------------------------------------------------------
virtual VkBuffer getVkBuffer() const
{ return m_billboard_buffer->getVkBuffer(); }
};
} // end namespace GE
#endif
@@ -0,0 +1,105 @@
#include "ge_vulkan_camera_scene_node.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_fbo_texture.hpp"
#include "ge_vulkan_scene_manager.hpp"
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanCameraSceneNode::GEVulkanCameraSceneNode(irr::scene::ISceneNode* parent,
irr::scene::ISceneManager* mgr,
irr::s32 id,
const irr::core::vector3df& position,
const irr::core::vector3df& lookat)
: CCameraSceneNode(parent, mgr, id, position, lookat)
{
static_cast<GEVulkanSceneManager*>(SceneManager)->addDrawCall(this);
} // GEVulkanCameraSceneNode
// ----------------------------------------------------------------------------
GEVulkanCameraSceneNode::~GEVulkanCameraSceneNode()
{
static_cast<GEVulkanSceneManager*>(SceneManager)->removeDrawCall(this);
} // ~GEVulkanCameraSceneNode
// ----------------------------------------------------------------------------
void GEVulkanCameraSceneNode::render()
{
irr::scene::CCameraSceneNode::render();
m_ubo_data.m_view_matrix = ViewArea.getTransform(irr::video::ETS_VIEW);
m_ubo_data.m_projection_matrix = ViewArea.getTransform(irr::video::ETS_PROJECTION);
// https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
// Vulkan clip space has inverted Y and half Z
irr::core::matrix4 clip;
clip[5] = -1.0f;
clip[10] = 0.5f;
clip[14] = 0.5f;
m_ubo_data.m_projection_matrix = clip * m_ubo_data.m_projection_matrix;
GEVulkanDriver* vk = getVKDriver();
if (!vk->getRTTTexture() || vk->getRTTTexture()->useSwapChainOutput())
{
m_ubo_data.m_projection_matrix = vk->getPreRotationMatrix() *
m_ubo_data.m_projection_matrix;
}
irr::core::matrix4 mat;
ViewArea.getTransform(irr::video::ETS_VIEW).getInverse(mat);
m_ubo_data.m_inverse_view_matrix = mat;
m_ubo_data.m_projection_matrix.getInverse(mat);
m_ubo_data.m_inverse_projection_matrix = mat;
mat = m_ubo_data.m_projection_matrix * m_ubo_data.m_view_matrix;
m_ubo_data.m_projection_view_matrix = mat;
m_ubo_data.m_projection_view_matrix.getInverse(
m_ubo_data.m_inverse_projection_view_matrix);
VkViewport vp = {};
float scale = getGEConfig()->m_render_scale;
if (vk->getSeparateRTTTexture())
scale = 1.0f;
vp.x = m_viewport.UpperLeftCorner.X * scale;
vp.y = m_viewport.UpperLeftCorner.Y * scale;
vp.width = m_viewport.getWidth() * scale;
vp.height = m_viewport.getHeight() * scale;
vk->getRotatedViewport(&vp, true/*handle_rtt*/);
m_ubo_data.m_viewport.UpperLeftCorner.X = vp.x;
m_ubo_data.m_viewport.UpperLeftCorner.Y = vp.y;
m_ubo_data.m_viewport.LowerRightCorner.X = vp.width;
m_ubo_data.m_viewport.LowerRightCorner.Y = vp.height;
if (!vk->getRTTTexture() || vk->getRTTTexture()->useSwapChainOutput())
{
vp.x = vp.y = 0.0f;
vp.width = vk->getCurrentRenderTargetSize().Width;
vp.height = vk->getCurrentRenderTargetSize().Height;
vk->getRotatedViewport(&vp, true/*handle_rtt*/);
m_ubo_data.m_screensize.UpperLeftCorner.X = vp.width;
m_ubo_data.m_screensize.UpperLeftCorner.Y = vp.height;
m_ubo_data.m_screensize.LowerRightCorner.X = 0.0;
m_ubo_data.m_screensize.LowerRightCorner.Y = 0.0;
}
else
{
m_ubo_data.m_screensize.UpperLeftCorner.X = vk->getRTTTexture()->getSize().Width;
m_ubo_data.m_screensize.UpperLeftCorner.Y = vk->getRTTTexture()->getSize().Height;
m_ubo_data.m_screensize.LowerRightCorner.X = 0.0;
m_ubo_data.m_screensize.LowerRightCorner.Y = 0.0;
}
} // render
// ----------------------------------------------------------------------------
irr::core::matrix4 GEVulkanCameraSceneNode::getPVM() const
{
// Use the original unedited matrix for culling
return ViewArea.getTransform(irr::video::ETS_PROJECTION) *
ViewArea.getTransform(irr::video::ETS_VIEW);
} // getPVM
}
@@ -0,0 +1,51 @@
#ifndef HEADER_GE_VULKAN_CAMERA_SCENE_NODE_HPP
#define HEADER_GE_VULKAN_CAMERA_SCENE_NODE_HPP
#include "../source/Irrlicht/CCameraSceneNode.h"
#include <array>
namespace GE
{
struct GEVulkanCameraUBO
{
irr::core::matrix4 m_view_matrix;
irr::core::matrix4 m_projection_matrix;
irr::core::matrix4 m_inverse_view_matrix;
irr::core::matrix4 m_inverse_projection_matrix;
irr::core::matrix4 m_projection_view_matrix;
irr::core::matrix4 m_inverse_projection_view_matrix;
irr::core::rectf m_viewport;
irr::core::rectf m_screensize;
};
class GEVulkanCameraSceneNode : public irr::scene::CCameraSceneNode
{
private:
GEVulkanCameraUBO m_ubo_data;
irr::core::rect<irr::s32> m_viewport;
public:
// ------------------------------------------------------------------------
GEVulkanCameraSceneNode(irr::scene::ISceneNode* parent,
irr::scene::ISceneManager* mgr, irr::s32 id,
const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0),
const irr::core::vector3df& lookat = irr::core::vector3df(0, 0, 100));
// ------------------------------------------------------------------------
~GEVulkanCameraSceneNode();
// ------------------------------------------------------------------------
virtual void render();
// ------------------------------------------------------------------------
void setViewPort(const irr::core::rect<irr::s32>& area)
{ m_viewport = area; }
// ------------------------------------------------------------------------
const irr::core::rect<irr::s32>& getViewPort() const { return m_viewport; }
// ------------------------------------------------------------------------
irr::core::matrix4 getPVM() const;
// ------------------------------------------------------------------------
const GEVulkanCameraUBO* const getUBOData() const { return &m_ubo_data; }
}; // GEVulkanCameraSceneNode
}
#endif
@@ -0,0 +1,266 @@
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_driver.hpp"
#include <atomic>
#include <condition_variable>
#include <cstdio>
#include <deque>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <stdexcept>
#include "../source/Irrlicht/os.h"
#ifndef thread_local
# if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
# define thread_local _Thread_local
# elif defined _WIN32 && ( \
defined _MSC_VER || \
defined __ICL || \
defined __DMC__ || \
defined __BORLANDC__ )
# define thread_local __declspec(thread)
/* note that ICC (linux) and Clang are covered by __GNUC__ */
# elif defined __GNUC__ || \
defined __SUNPRO_C || \
defined __xlC__
# define thread_local __thread
# else
# error "Cannot define thread_local"
# endif
#endif
namespace GE
{
namespace GEVulkanCommandLoader
{
// ============================================================================
GEVulkanDriver* g_vk = NULL;
std::mutex g_loaders_mutex;
std::condition_variable g_loaders_cv;
std::vector<std::thread> g_loaders;
std::deque<std::function<void()> > g_threaded_commands;
thread_local int g_loader_id = 0;
std::atomic_uint g_loader_count(0);
std::vector<VkCommandPool> g_command_pools;
std::vector<VkFence> g_command_fences;
std::vector<std::unique_ptr<std::atomic<bool> > > g_thread_idle;
} // GEVulkanCommandLoader
// ============================================================================
void GEVulkanCommandLoader::init(GEVulkanDriver* vk)
{
g_vk = vk;
unsigned thread_count = std::thread::hardware_concurrency();
if (thread_count == 0)
thread_count = 3;
else
thread_count += 3;
g_command_pools.resize(thread_count);
g_command_fences.resize(thread_count);
for (unsigned i = 0; i < thread_count; i++)
{
VkCommandPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_info.queueFamilyIndex = g_vk->getGraphicsFamily();
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VkResult result = vkCreateCommandPool(g_vk->getDevice(), &pool_info,
NULL, &g_command_pools[i]);
if (result != VK_SUCCESS)
{
throw std::runtime_error(
"GEVulkanCommandLoader: vkCreateCommandPool failed");
}
VkFenceCreateInfo fence_info = {};
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
result = vkCreateFence(g_vk->getDevice(), &fence_info, NULL,
&g_command_fences[i]);
if (result != VK_SUCCESS)
{
throw std::runtime_error(
"GEVulkanCommandLoader: vkCreateFence failed");
}
}
for (unsigned i = 0; i < thread_count - 1; i++)
{
std::unique_ptr<std::atomic<bool> > idle;
idle.reset(new std::atomic<bool>(true));
g_thread_idle.push_back(std::move(idle));
}
g_loader_count.store(thread_count);
for (unsigned i = 0; i < thread_count - 1; i++)
{
g_loaders.emplace_back(
[i]()->void
{
g_loader_id = i + 1;
while (true)
{
g_thread_idle[i]->store(true);
std::unique_lock<std::mutex> ul(g_loaders_mutex);
g_loaders_cv.wait(ul, []
{
return !g_threaded_commands.empty();
});
if (g_loader_count.load() == 0)
return;
g_thread_idle[i]->store(false);
std::function<void()> copied = g_threaded_commands.front();
g_threaded_commands.pop_front();
ul.unlock();
copied();
}
});
}
char thread_count_str[40] = {};
snprintf(thread_count_str, 40, "%d threads used, %d graphics queue(s)",
thread_count - 1, vk->getGraphicsQueueCount());
os::Printer::log("Vulkan command loader", thread_count_str);
} // init
// ----------------------------------------------------------------------------
void GEVulkanCommandLoader::destroy()
{
g_loader_count.store(0);
if (!g_loaders.empty())
{
std::unique_lock<std::mutex> ul(g_loaders_mutex);
g_threaded_commands.push_back([](){});
g_loaders_cv.notify_all();
ul.unlock();
for (std::thread& t : g_loaders)
t.join();
g_loaders.clear();
}
for (auto& f : g_threaded_commands)
f();
g_threaded_commands.clear();
g_thread_idle.clear();
for (VkCommandPool& pool : g_command_pools)
vkDestroyCommandPool(g_vk->getDevice(), pool, NULL);
g_command_pools.clear();
for (VkFence& fence : g_command_fences)
vkDestroyFence(g_vk->getDevice(), fence, NULL);
g_command_fences.clear();
} // destroy
// ----------------------------------------------------------------------------
bool GEVulkanCommandLoader::multiThreadingEnabled()
{
return g_loader_count.load() != 0;
} // enabled
// ----------------------------------------------------------------------------
bool GEVulkanCommandLoader::isUsingMultiThreadingNow()
{
return g_loader_id != 0;
} // isUsingMultiThreadingNow
// ----------------------------------------------------------------------------
unsigned GEVulkanCommandLoader::getLoaderCount()
{
return g_loader_count.load();
} // getLoaderCount
// ----------------------------------------------------------------------------
int GEVulkanCommandLoader::getLoaderId()
{
return g_loader_id;
} // getLoaderId
// ----------------------------------------------------------------------------
VkCommandPool GEVulkanCommandLoader::getCurrentCommandPool()
{
return g_command_pools[g_loader_id];
} // getCurrentCommandPool
// ----------------------------------------------------------------------------
VkFence GEVulkanCommandLoader::getCurrentFence()
{
return g_command_fences[g_loader_id];
} // getCurrentFence
// ----------------------------------------------------------------------------
void GEVulkanCommandLoader::addMultiThreadingCommand(std::function<void()> cmd)
{
if (g_loaders.empty())
return;
std::lock_guard<std::mutex> lock(g_loaders_mutex);
g_threaded_commands.push_back(cmd);
g_loaders_cv.notify_one();
} // addMultiThreadingCommand
// ----------------------------------------------------------------------------
VkCommandBuffer GEVulkanCommandLoader::beginSingleTimeCommands()
{
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
alloc_info.commandPool = g_command_pools[g_loader_id];
alloc_info.commandBufferCount = 1;
VkCommandBuffer command_buffer;
vkAllocateCommandBuffers(g_vk->getDevice(), &alloc_info, &command_buffer);
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(command_buffer, &begin_info);
return command_buffer;
} // beginSingleTimeCommands
// ----------------------------------------------------------------------------
void GEVulkanCommandLoader::endSingleTimeCommands(VkCommandBuffer command_buffer,
VkQueueFlagBits bit)
{
vkEndCommandBuffer(command_buffer);
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer;
const int loader_id = g_loader_id;
VkQueue queue = VK_NULL_HANDLE;
std::unique_lock<std::mutex> lock = g_vk->getGraphicsQueue(&queue);
vkQueueSubmit(queue, 1, &submit_info, g_command_fences[loader_id]);
lock.unlock();
vkWaitForFences(g_vk->getDevice(), 1, &g_command_fences[loader_id],
VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(g_vk->getDevice(), 1, &g_command_fences[loader_id]);
vkFreeCommandBuffers(g_vk->getDevice(), g_command_pools[loader_id], 1,
&command_buffer);
} // endSingleTimeCommands
// ----------------------------------------------------------------------------
void GEVulkanCommandLoader::waitIdle()
{
while (true)
{
std::lock_guard<std::mutex> lock(g_loaders_mutex);
if (g_threaded_commands.empty())
break;
}
unsigned i = 0;
while (i < g_thread_idle.size())
{
if (g_thread_idle[i]->load() == false)
continue;
i++;
}
} // waitIdle
}
@@ -0,0 +1,42 @@
#ifndef HEADER_GE_VULKAN_COMMAND_LOADER_HPP
#define HEADER_GE_VULKAN_COMMAND_LOADER_HPP
#include "vulkan_wrapper.h"
#include <functional>
namespace GE
{
class GEVulkanDriver;
namespace GEVulkanCommandLoader
{
// ----------------------------------------------------------------------------
void init(GEVulkanDriver*);
// ----------------------------------------------------------------------------
void destroy();
// ----------------------------------------------------------------------------
bool multiThreadingEnabled();
// ----------------------------------------------------------------------------
bool isUsingMultiThreadingNow();
// ----------------------------------------------------------------------------
unsigned getLoaderCount();
// ----------------------------------------------------------------------------
int getLoaderId();
// ----------------------------------------------------------------------------
VkCommandPool getCurrentCommandPool();
// ----------------------------------------------------------------------------
VkFence getCurrentFence();
// ----------------------------------------------------------------------------
void addMultiThreadingCommand(std::function<void()> cmd);
// ----------------------------------------------------------------------------
VkCommandBuffer beginSingleTimeCommands();
// ----------------------------------------------------------------------------
void endSingleTimeCommands(VkCommandBuffer command_buffer,
VkQueueFlagBits bit = VK_QUEUE_GRAPHICS_BIT);
// ----------------------------------------------------------------------------
void waitIdle();
}; // GEVulkanCommandLoader
}
#endif
@@ -0,0 +1,827 @@
#include "ge_vulkan_deferred_fbo.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_attachment_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_driver.hpp"
#include <array>
#include <exception>
#include <stdexcept>
namespace GE
{
GEVulkanDeferredFBO::GEVulkanDeferredFBO(GEVulkanDriver* vk,
const core::dimension2d<u32>& size,
bool swapchain_output)
: GEVulkanFBOTexture(vk, size,
!(!vk->getSeparateRTTTexture() &&
getGEConfig()->m_auto_deferred_type == GADT_DISPLACE)),
m_swapchain_output(swapchain_output)
{
m_attachments = {};
m_descriptor_layout.fill(VK_NULL_HANDLE);
m_descriptor_pool.fill(VK_NULL_HANDLE);
m_descriptor_set.fill(VK_NULL_HANDLE);
for (unsigned i = GVDFT_COLOR; i <= GVDFT_NORMAL; i++)
{
m_attachments[i] = new GEVulkanAttachmentTexture(vk, size,
VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
VK_IMAGE_ASPECT_COLOR_BIT);
}
std::vector<VkFormat> hdr_formats =
{
VK_FORMAT_B10G11R11_UFLOAT_PACK32,
VK_FORMAT_R16G16B16A16_SFLOAT,
VK_FORMAT_B8G8R8A8_UNORM
};
VkFormat hdr_format = vk->findSupportedFormat(hdr_formats,
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
m_attachments[GVDFT_HDR] = new GEVulkanAttachmentTexture(vk, size,
hdr_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT,
VK_IMAGE_ASPECT_COLOR_BIT);
if (!vk->getSeparateRTTTexture() &&
getGEConfig()->m_auto_deferred_type == GADT_DISPLACE)
{
std::vector<VkFormat> displace_mask_formats =
{
VK_FORMAT_R8G8_UNORM,
VK_FORMAT_B8G8R8A8_UNORM
};
VkFormat displace_mask_format = vk->findSupportedFormat(
displace_mask_formats, VK_IMAGE_TILING_OPTIMAL,
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
m_attachments[GVDFT_DISPLACE_MASK] = new GEVulkanAttachmentTexture(vk,
size, displace_mask_format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
if (getGEConfig()->m_screen_space_reflection_type != GSSRT_DISABLED)
{
m_attachments[GVDFT_DISPLACE_SSR] =
new GEVulkanAttachmentTexture(vk, size,
VK_FORMAT_B8G8R8A8_UNORM,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
}
VkCommandBuffer command_buffer =
GEVulkanCommandLoader::beginSingleTimeCommands();
m_attachments[GVDFT_DISPLACE_MASK]->transitionImageLayout(
command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (getAttachment<GVDFT_DISPLACE_SSR>())
{
getAttachment<GVDFT_DISPLACE_SSR>()->transitionImageLayout(
command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
m_attachments[GVDFT_DISPLACE_COLOR] = new GEVulkanAttachmentTexture(vk,
size, VK_FORMAT_B8G8R8A8_UNORM,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VK_IMAGE_ASPECT_COLOR_BIT);
}
// m_descriptor_layout[GVDFP_HDR]
std::array<VkDescriptorSetLayoutBinding, 3> texture_layout_binding = {};
texture_layout_binding[0].binding = 0;
texture_layout_binding[0].descriptorCount = 1;
texture_layout_binding[0].descriptorType =
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
texture_layout_binding[0].pImmutableSamplers = NULL;
texture_layout_binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_layout_binding[1] = texture_layout_binding[0];
texture_layout_binding[1].binding = 1;
texture_layout_binding[2] = texture_layout_binding[0];
texture_layout_binding[2].binding = 2;
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.flags = 0;
setinfo.pNext = NULL;
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = texture_layout_binding.data();
setinfo.bindingCount = texture_layout_binding.size();
if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo,
NULL, &m_descriptor_layout[GVDFP_HDR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorSetLayout failed for "
"GVDFP_HDR in GEVulkanDeferredFBO");
}
// m_descriptor_pool[GVDFP_HDR]
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
pool_size.descriptorCount = texture_layout_binding.size();
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = 1;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL,
&m_descriptor_pool[GVDFP_HDR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"GVDFP_HDR in GEVulkanDeferredFBO");
}
// m_descriptor_set[GVDFP_HDR]
std::vector<VkDescriptorSetLayout> layouts(1,
m_descriptor_layout[GVDFP_HDR]);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool[GVDFP_HDR];
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info,
&m_descriptor_set[GVDFP_HDR]) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"GVDFP_HDR in GEVulkanDeferredFBO");
}
std::array<VkDescriptorImageInfo, 3> image_infos = {};
image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[0].imageView =
(VkImageView)m_attachments[GVDFT_COLOR]->getTextureHandler();
image_infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[1].imageView =
(VkImageView)m_attachments[GVDFT_NORMAL]->getTextureHandler();
image_infos[2].imageLayout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
image_infos[2].imageView =
(VkImageView)m_depth_texture->getTextureHandler();
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
write_descriptor_set.descriptorCount = image_infos.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_descriptor_set[GVDFP_HDR];
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
initConvertColorDescriptor(vk);
if (getAttachment<GVDFT_DISPLACE_COLOR>())
initDisplaceDescriptor(vk);
} // GEVulkanDeferredFBO
// ----------------------------------------------------------------------------
GEVulkanDeferredFBO::~GEVulkanDeferredFBO()
{
for (GEVulkanAttachmentTexture* t : m_attachments)
delete t;
for (VkDescriptorPool pool : m_descriptor_pool)
{
if (pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(m_vk->getDevice(), pool, NULL);
}
for (VkDescriptorSetLayout descriptor_layout : m_descriptor_layout)
{
if (descriptor_layout != VK_NULL_HANDLE)
{
vkDestroyDescriptorSetLayout(m_vk->getDevice(), descriptor_layout,
NULL);
}
}
} // ~GEVulkanDeferredFBO
// ----------------------------------------------------------------------------
void GEVulkanDeferredFBO::initConvertColorDescriptor(GEVulkanDriver* vk)
{
// m_descriptor_layout[GVDFP_CONVERT_COLOR]
std::array<VkDescriptorSetLayoutBinding, 1> texture_layout_binding = {};
texture_layout_binding[0].binding = 0;
texture_layout_binding[0].descriptorCount = 1;
texture_layout_binding[0].descriptorType =
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
texture_layout_binding[0].pImmutableSamplers = NULL;
texture_layout_binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.flags = 0;
setinfo.pNext = NULL;
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = texture_layout_binding.data();
setinfo.bindingCount = texture_layout_binding.size();
if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo,
NULL, &m_descriptor_layout[GVDFP_CONVERT_COLOR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorSetLayout failed for "
"GVDFP_CONVERT_COLOR in GEVulkanDeferredFBO");
}
// m_descriptor_pool[GVDFP_CONVERT_COLOR]
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
pool_size.descriptorCount = texture_layout_binding.size();
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = 1;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL,
&m_descriptor_pool[GVDFP_CONVERT_COLOR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"GVDFP_CONVERT_COLOR in GEVulkanDeferredFBO");
}
// m_descriptor_set[GVDFP_CONVERT_COLOR]
std::vector<VkDescriptorSetLayout> layouts(1,
m_descriptor_layout[GVDFP_CONVERT_COLOR]);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool[GVDFP_CONVERT_COLOR];
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info,
&m_descriptor_set[GVDFP_CONVERT_COLOR]) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"GVDFP_CONVERT_COLOR in GEVulkanDeferredFBO");
}
std::array<VkDescriptorImageInfo, 1> image_infos = {};
image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[0].imageView =
(VkImageView)m_attachments[GVDFT_HDR]->getTextureHandler();
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
write_descriptor_set.descriptorCount = image_infos.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_descriptor_set[GVDFP_CONVERT_COLOR];
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
} // initConvertColorDescriptor
// ----------------------------------------------------------------------------
void GEVulkanDeferredFBO::initDisplaceDescriptor(GEVulkanDriver* vk)
{
// m_descriptor_layout[GVDFP_DISPLACE_COLOR]
std::array<VkDescriptorSetLayoutBinding, 3> texture_layout_binding = {};
texture_layout_binding[0].binding = 0;
texture_layout_binding[0].descriptorCount = 1;
texture_layout_binding[0].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
texture_layout_binding[0].pImmutableSamplers = NULL;
texture_layout_binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_layout_binding[1] = texture_layout_binding[0];
texture_layout_binding[1].binding = 1;
texture_layout_binding[2] = texture_layout_binding[0];
texture_layout_binding[2].binding = 2;
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.flags = 0;
setinfo.pNext = NULL;
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = texture_layout_binding.data();
setinfo.bindingCount = texture_layout_binding.size();
if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo,
NULL, &m_descriptor_layout[GVDFP_DISPLACE_COLOR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorSetLayout failed for "
"GVDFP_DISPLACE_COLOR in GEVulkanDeferredFBO");
}
int hiz_multi =
getGEConfig()->m_screen_space_reflection_type <= GSSRT_FAST ? 2 : 1;
// m_descriptor_pool[GVDFP_DISPLACE_COLOR]
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptorCount = texture_layout_binding.size() * hiz_multi;
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = hiz_multi;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL,
&m_descriptor_pool[GVDFP_DISPLACE_COLOR]) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"GVDFP_DISPLACE_COLOR in GEVulkanDeferredFBO");
}
// m_descriptor_set[GVDFP_DISPLACE_MASK + GVDFP_DISPLACE_COLOR]
std::vector<VkDescriptorSetLayout> layouts(hiz_multi,
m_descriptor_layout[GVDFP_DISPLACE_COLOR]);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool[GVDFP_DISPLACE_COLOR];
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info,
hiz_multi == 1 ? &m_descriptor_set[GVDFP_DISPLACE_COLOR] :
&m_descriptor_set[GVDFP_DISPLACE_MASK]) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"GVDFP_DISPLACE_MASK + GVDFP_DISPLACE_COLOR in "
"GEVulkanDeferredFBO");
}
std::array<VkDescriptorImageInfo, texture_layout_binding.size()>
image_infos = {};
image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[0].imageView =
(VkImageView)m_attachments[GVDFT_DISPLACE_MASK]->getTextureHandler();
image_infos[0].sampler = m_vk->getSampler(GVS_NEAREST);
image_infos[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[1].imageView = m_attachments[GVDFT_DISPLACE_SSR] ?
(VkImageView)m_attachments[GVDFT_DISPLACE_SSR]->getTextureHandler() :
(VkImageView)m_vk->getTransparentTexture()->getTextureHandler();
image_infos[1].sampler = m_vk->getSampler(GVS_NEAREST);
image_infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[2].imageView =
(VkImageView)m_attachments[GVDFT_DISPLACE_COLOR]->getTextureHandler();
image_infos[2].sampler = m_vk->getSampler(GVS_NEAREST);
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = image_infos.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_descriptor_set[GVDFP_DISPLACE_COLOR];
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
if (hiz_multi == 1)
return;
image_infos[0] = image_infos[2];
image_infos[1].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
image_infos[1].imageView =
(VkImageView)m_depth_texture->getTextureHandler();
image_infos[1].sampler = m_vk->getSampler(GVS_SHADOW);
image_infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[2].imageView =
(VkImageView)m_vk->getTransparentTexture()->getTextureHandler();
write_descriptor_set.dstSet = m_descriptor_set[GVDFP_DISPLACE_MASK];
image_infos[2].sampler = m_vk->getSampler(GVS_SKYBOX);
vkUpdateDescriptorSets(vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
} // initDisplaceDescriptor
// ----------------------------------------------------------------------------
void GEVulkanDeferredFBO::createRTT()
{
if (!useSwapChainOutput())
createOutputImage();
std::array<VkAttachmentDescription, 5> attachment_desc = {};
// HDR attachment
attachment_desc[0].format = m_attachments[GVDFT_HDR]->getInternalFormat();
attachment_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_desc[0].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_desc[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Depth attachment
attachment_desc[1].format = m_depth_texture->getInternalFormat();
attachment_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
if (getAttachment<GVDFT_DISPLACE_COLOR>())
attachment_desc[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
else
attachment_desc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (getAttachment<GVDFT_DISPLACE_COLOR>())
attachment_desc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
else
attachment_desc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
// Color / normal (mixed with pbr data) attachment
attachment_desc[2].format = VK_FORMAT_B8G8R8A8_UNORM;
attachment_desc[2].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_desc[2].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_desc[2].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachment_desc[3] = attachment_desc[2];
// Output / swapchain attachment
bool single_pass_swapchain =
useSwapChainOutput() && !getAttachment<GVDFT_DISPLACE_COLOR>();
attachment_desc[4] = attachment_desc[2];
attachment_desc[4].format = single_pass_swapchain ?
m_vk->getSwapChainImageFormat() : VK_FORMAT_B8G8R8A8_UNORM;
attachment_desc[4].finalLayout = single_pass_swapchain ?
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR :
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachment_desc[4].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
VkAttachmentReference hdr_reference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkAttachmentReference depth_reference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
std::array<VkAttachmentReference, 2> pbr_reference =
{{
{ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL },
{ 3, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }
}};
std::array<VkSubpassDescription, 3> subpass_desc = {};
subpass_desc[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_desc[0].colorAttachmentCount = pbr_reference.size();
subpass_desc[0].pColorAttachments = pbr_reference.data();
subpass_desc[0].pDepthStencilAttachment = &depth_reference;
std::array<VkAttachmentReference, 3> input_reference =
{{
{ 2, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
{ 3, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
{ 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL },
}};
subpass_desc[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_desc[1].colorAttachmentCount = 1;
subpass_desc[1].pColorAttachments = &hdr_reference;
VkAttachmentReference depth_reference_read_only = depth_reference;
depth_reference_read_only.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
subpass_desc[1].pDepthStencilAttachment = &depth_reference_read_only;
subpass_desc[1].inputAttachmentCount = input_reference.size();
subpass_desc[1].pInputAttachments = input_reference.data();
VkAttachmentReference final_reference = { 4, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
subpass_desc[2].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_desc[2].colorAttachmentCount = 1;
subpass_desc[2].pColorAttachments = &final_reference;
VkAttachmentReference final_input_reference = { 0, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
subpass_desc[2].inputAttachmentCount = 1;
subpass_desc[2].pInputAttachments = &final_input_reference;
subpass_desc[2].pDepthStencilAttachment = &depth_reference;
// Create the actual render pass
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = attachment_desc.size();
render_pass_info.pAttachments = attachment_desc.data();
render_pass_info.subpassCount = subpass_desc.size();
render_pass_info.pSubpasses = subpass_desc.data();
std::vector<VkSubpassDependency> dependencies(4);
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].dstSubpass = 1;
dependencies[1].srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].srcAccessMask = 0;
dependencies[1].dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[2].srcSubpass = 0;
dependencies[2].dstSubpass = 1;
dependencies[2].srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[2].dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[2].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[2].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[2].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[3].srcSubpass = 1;
dependencies[3].dstSubpass = 2;
dependencies[3].srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[3].dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[3].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[3].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[3].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
if (single_pass_swapchain)
{
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 2;
dependency.srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies.push_back(dependency);
}
if (getAttachment<GVDFT_DISPLACE_COLOR>())
{
VkSubpassDependency dependency = {};
dependency.srcSubpass = 2;
dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
dependency.srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependency.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependency.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies.push_back(dependency);
}
render_pass_info.dependencyCount = dependencies.size();
render_pass_info.pDependencies = dependencies.data();
m_rtt_render_pass.resize(1, VK_NULL_HANDLE);
if (vkCreateRenderPass(m_vk->getDevice(), &render_pass_info, NULL,
&m_rtt_render_pass[0]) != VK_SUCCESS)
throw std::runtime_error("vkCreateRenderPass failed in createRTT");
std::vector<std::array<VkImageView, attachment_desc.size()> > attachments(1);
auto& sciv = m_vk->getSwapChainImageViews();
if (single_pass_swapchain)
attachments.resize(sciv.size());
m_rtt_frame_buffer.resize(attachments.size(), VK_NULL_HANDLE);
for (unsigned i = 0; i < attachments.size(); i++)
{
attachments[i] =
{{
(VkImageView)m_attachments[GVDFT_HDR]->getTextureHandler(),
(VkImageView)m_depth_texture->getTextureHandler(),
(VkImageView)m_attachments[GVDFT_COLOR]->getTextureHandler(),
(VkImageView)m_attachments[GVDFT_NORMAL]->getTextureHandler(),
VK_NULL_HANDLE
}};
if (getAttachment<GVDFT_DISPLACE_COLOR>())
{
attachments[i][4] = (VkImageView)
getAttachment<GVDFT_DISPLACE_COLOR>()->getTextureHandler();
}
else if (useSwapChainOutput())
attachments[i][4] = sciv[i];
else
attachments[i][4] = (VkImageView)getTextureHandler();
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = m_rtt_render_pass[0];
framebuffer_info.attachmentCount = attachments[i].size();
framebuffer_info.pAttachments = attachments[i].data();
framebuffer_info.width = m_depth_texture->getSize().Width;
framebuffer_info.height = m_depth_texture->getSize().Height;
framebuffer_info.layers = 1;
if (vkCreateFramebuffer(m_vk->getDevice(), &framebuffer_info,
NULL, &m_rtt_frame_buffer[i]) != VK_SUCCESS)
throw std::runtime_error("vkCreateFramebuffer failed in createRTT");
}
if (getAttachment<GVDFT_DISPLACE_COLOR>())
createDisplacePasses();
} // createRTT
// ----------------------------------------------------------------------------
void GEVulkanDeferredFBO::createDisplacePasses()
{
m_rtt_render_pass.resize(GVDFP_COUNT, VK_NULL_HANDLE);
// m_rtt_render_pass[GVDFP_DISPLACE_MASK]
{
std::vector<VkAttachmentDescription> attachment_desc(1);
attachment_desc[0].format = m_attachments[GVDFT_DISPLACE_MASK]->getInternalFormat();
attachment_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_desc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment_desc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[0].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachment_desc[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if (getAttachment<GVDFT_DISPLACE_SSR>())
{
attachment_desc.push_back(attachment_desc[0]);
attachment_desc.back().format =
getAttachment<GVDFT_DISPLACE_SSR>()->getInternalFormat();
}
VkAttachmentDescription depth_desc = {};
depth_desc.format = m_depth_texture->getInternalFormat();
depth_desc.samples = VK_SAMPLE_COUNT_1_BIT;
depth_desc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depth_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depth_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depth_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depth_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
depth_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
attachment_desc.push_back(depth_desc);
VkAttachmentReference depth_reference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL };
std::vector<VkAttachmentReference> color_references =
{{ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }};
if (getAttachment<GVDFT_DISPLACE_SSR>())
{
color_references.push_back({ 1, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
depth_reference.attachment = 2;
}
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = color_references.size();
subpass.pColorAttachments = color_references.data();
subpass.pDepthStencilAttachment = &depth_reference;
std::array<VkSubpassDependency, 1> dependencies = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = attachment_desc.size();
render_pass_info.pAttachments = attachment_desc.data();
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass;
render_pass_info.dependencyCount = dependencies.size();
render_pass_info.pDependencies = dependencies.data();
if (vkCreateRenderPass(m_vk->getDevice(), &render_pass_info, NULL,
&m_rtt_render_pass[GVDFP_DISPLACE_MASK]) != VK_SUCCESS)
throw std::runtime_error("vkCreateRenderPass failed for GVDFP_DISPLACE_MASK");
}
// m_rtt_render_pass[GVDFP_DISPLACE_COLOR]
{
std::array<VkAttachmentDescription, 2> attachment_desc = {};
attachment_desc[0].format = useSwapChainOutput() ?
m_vk->getSwapChainImageFormat() : VK_FORMAT_B8G8R8A8_UNORM;
attachment_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment_desc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment_desc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachment_desc[0].finalLayout = useSwapChainOutput() ?
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
attachment_desc[1].format = m_depth_texture->getInternalFormat();
attachment_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
attachment_desc[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment_desc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachment_desc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachment_desc[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
attachment_desc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_reference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkAttachmentReference depth_reference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL };
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pDepthStencilAttachment = &depth_reference;
std::array<VkSubpassDependency, 1> dependencies = {};
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = attachment_desc.size();
render_pass_info.pAttachments = attachment_desc.data();
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass;
render_pass_info.dependencyCount = dependencies.size();
render_pass_info.pDependencies = dependencies.data();
if (vkCreateRenderPass(m_vk->getDevice(), &render_pass_info, NULL,
&m_rtt_render_pass[GVDFP_DISPLACE_COLOR]) != VK_SUCCESS)
throw std::runtime_error("vkCreateRenderPass failed for GVDFP_DISPLACE_COLOR");
}
m_rtt_frame_buffer.resize(GVDFP_COUNT, VK_NULL_HANDLE);
auto& sciv = m_vk->getSwapChainImageViews();
if (useSwapChainOutput())
{
for (unsigned i = 0; i < sciv.size() - 1; i++)
m_rtt_frame_buffer.push_back(VK_NULL_HANDLE);
}
// m_rtt_frame_buffer[GVDFP_DISPLACE_MASK]
{
std::vector<VkImageView> attachments =
{
(VkImageView)m_attachments[GVDFT_DISPLACE_MASK]->getTextureHandler()
};
if (getAttachment<GVDFT_DISPLACE_SSR>())
{
attachments.push_back((VkImageView)
m_attachments[GVDFT_DISPLACE_SSR]->getTextureHandler());
}
attachments.push_back((VkImageView)m_depth_texture->getTextureHandler());
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = m_rtt_render_pass[GVDFP_DISPLACE_MASK];
framebuffer_info.attachmentCount = attachments.size();
framebuffer_info.pAttachments = attachments.data();
framebuffer_info.width = m_depth_texture->getSize().Width;
framebuffer_info.height = m_depth_texture->getSize().Height;
framebuffer_info.layers = 1;
if (vkCreateFramebuffer(m_vk->getDevice(), &framebuffer_info, NULL,
&m_rtt_frame_buffer[GVDFP_DISPLACE_MASK]) != VK_SUCCESS)
throw std::runtime_error("vkCreateFramebuffer failed for GVDFP_DISPLACE_MASK");
}
// m_rtt_frame_buffer[GVDFP_DISPLACE_COLOR]
for (unsigned i = GVDFP_DISPLACE_COLOR; i < m_rtt_frame_buffer.size(); i++)
{
std::array<VkImageView, 2> attachments =
{{
useSwapChainOutput() ? sciv[i - GVDFP_DISPLACE_COLOR] : (VkImageView)getTextureHandler(),
(VkImageView)m_depth_texture->getTextureHandler()
}};
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = m_rtt_render_pass[GVDFP_DISPLACE_COLOR];
framebuffer_info.attachmentCount = attachments.size();
framebuffer_info.pAttachments = attachments.data();
framebuffer_info.width = m_depth_texture->getSize().Width;
framebuffer_info.height = m_depth_texture->getSize().Height;
framebuffer_info.layers = 1;
if (vkCreateFramebuffer(m_vk->getDevice(), &framebuffer_info, NULL,
&m_rtt_frame_buffer[i]) != VK_SUCCESS)
throw std::runtime_error("vkCreateFramebuffer failed for GVDFP_DISPLACE_COLOR");
}
} // createDisplacePasses
}
@@ -0,0 +1,102 @@
#ifndef HEADER_GE_VULKAN_DEFERRED_FBO_HPP
#define HEADER_GE_VULKAN_DEFERRED_FBO_HPP
#include "ge_vulkan_fbo_texture.hpp"
#include <array>
namespace GE
{
enum GEVulkanDeferredFBOType : unsigned
{
GVDFT_COLOR = 0,
GVDFT_NORMAL,
GVDFT_HDR,
GVDFT_DISPLACE_MASK,
GVDFT_DISPLACE_SSR,
GVDFT_DISPLACE_COLOR,
GVDFT_COUNT,
};
enum GEVulkanDeferredFBOPass : unsigned
{
GVDFP_HDR = 0,
GVDFP_CONVERT_COLOR,
GVDFP_DISPLACE_MASK,
GVDFP_DISPLACE_COLOR,
GVDFP_COUNT,
};
class GEVulkanDeferredFBO : public GEVulkanFBOTexture
{
private:
std::array<GEVulkanAttachmentTexture*, GVDFT_COUNT> m_attachments;
std::array<VkDescriptorSetLayout, GVDFP_COUNT> m_descriptor_layout;
std::array<VkDescriptorPool, GVDFP_COUNT> m_descriptor_pool;
std::array<VkDescriptorSet, GVDFP_COUNT> m_descriptor_set;
const bool m_swapchain_output;
// ------------------------------------------------------------------------
void initConvertColorDescriptor(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void initDisplaceDescriptor(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void createDisplacePasses();
public:
// ------------------------------------------------------------------------
GEVulkanDeferredFBO(GEVulkanDriver* vk, const core::dimension2d<u32>& size,
bool swapchain_output);
// ------------------------------------------------------------------------
virtual ~GEVulkanDeferredFBO();
// ------------------------------------------------------------------------
virtual void createRTT();
// ------------------------------------------------------------------------
virtual bool isDeferredFBO() const { return true; }
// ------------------------------------------------------------------------
virtual bool useSwapChainOutput() const { return m_swapchain_output; }
// ------------------------------------------------------------------------
virtual unsigned getZeroClearCountForPass(unsigned pass) const
{
switch (pass)
{
case GVDFP_HDR:
{
unsigned count = 0;
for (unsigned i = 0; i < m_attachments.size(); i++)
{
if (i == GVDFT_HDR)
break;
GEVulkanAttachmentTexture* t = m_attachments[i];
if (t)
count++;
}
return count;
}
case GVDFP_DISPLACE_MASK:
return getAttachment<GVDFT_DISPLACE_SSR>() ? 2 : 1;
case GVDFP_DISPLACE_COLOR:
return 1;
default:
return GEVulkanFBOTexture::getZeroClearCountForPass(pass);
}
}
// ------------------------------------------------------------------------
virtual VkDescriptorSetLayout getDescriptorSetLayout(unsigned id) const
{ return m_descriptor_layout.at(id); }
// ------------------------------------------------------------------------
virtual const VkDescriptorSet* getDescriptorSet(unsigned id) const
{ return &m_descriptor_set.at(id); }
// ------------------------------------------------------------------------
template<unsigned AttachmentType>
GEVulkanAttachmentTexture* getAttachment() const
{
return std::get<AttachmentType>(m_attachments);
}
}; // GEVulkanDeferredFBO
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,341 @@
#ifndef HEADER_GE_VULKAN_DRAW_CALL_HPP
#define HEADER_GE_VULKAN_DRAW_CALL_HPP
#include <array>
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "vulkan_wrapper.h"
#include "matrix4.h"
#include "vector3d.h"
#include "ESceneNodeTypes.h"
#include "SColor.h"
#include "SMaterial.h"
#include "LinearMath/btQuaternion.h"
namespace irr
{
namespace scene
{
class ISceneNode; class IBillboardSceneNode; struct SParticle;
class IMesh; class ILightSceneNode;
}
}
namespace GE
{
class GECullingTool;
class GESPMBuffer;
class GEVulkanAnimatedMeshSceneNode;
class GEVulkanCameraSceneNode;
class GEVulkanDriver;
class GEVulkanDynamicBuffer;
class GEVulkanDynamicSPMBuffer;
class GEVulkanLightHandler;
class GEVulkanSkyBoxRenderer;
class GEVulkanTextureDescriptor;
typedef std::pair<std::vector<VkVertexInputBindingDescription>,
std::vector<VkVertexInputAttributeDescription> > VertexDescription;
struct ObjectData
{
float m_translation_x;
float m_translation_y;
float m_translation_z;
float m_hue_change;
float m_rotation[4];
float m_scale_x;
float m_scale_y;
float m_scale_z;
irr::video::SColor m_custom_vertex_color;
int m_skinning_offset;
int m_material_id;
float m_texture_trans[2];
// ------------------------------------------------------------------------
void init(irr::scene::ISceneNode* node, int material_id,
int skinning_offset, int irrlicht_material_id);
// ------------------------------------------------------------------------
void init(irr::scene::IBillboardSceneNode* node, int material_id,
const btQuaternion& rotation);
// ------------------------------------------------------------------------
void init(const irr::scene::SParticle& particle, int material_id,
const btQuaternion& rotation,
const irr::core::vector3df& view_position, bool flips,
bool sky_particle, bool backface_culling);
};
enum GEVulkanPipelineType : unsigned
{
GVPT_DEPTH = 1,
GVPT_SOLID,
GVPT_DEFERRED_LIGHTING,
GVPT_DEFERRED_CONVERT_COLOR,
GVPT_GHOST_DEPTH,
GVPT_TRANSPARENT,
GVPT_SKYBOX,
GVPT_DISPLACE_MASK,
GVPT_DISPLACE_COLOR,
};
struct GEMaterial;
struct PipelineSettings
{
std::string m_shader_name;
std::shared_ptr<const GEMaterial> m_material;
char m_drawing_priority;
VkPipelineLayout m_custom_pl;
VkCompareOp m_depth_op;
VkPrimitiveTopology m_topology;
VertexDescription m_vertex_description;
GEVulkanPipelineType m_pipeline_type;
PipelineSettings();
void loadMaterial(const GEMaterial& m);
};
struct PipelineData
{
PipelineSettings m_settings;
std::map<GEVulkanPipelineType, std::shared_ptr<VkPipeline> > m_pipelines;
};
struct DrawCallData
{
VkDrawIndexedIndirectCommand m_cmd;
std::string m_shader;
std::string m_sorting_key;
GESPMBuffer* m_mb;
int m_material_id;
uint32_t m_dynamic_offset;
};
class GEVulkanHiZDepth;
class GEVulkanDrawCall
{
private:
typedef std::array<const irr::video::ITexture*,
_IRR_MATERIAL_MAX_TEXTURES_> TexturesList;
const int BILLBOARD_NODE = -1;
const int PARTICLE_NODE = -2;
std::map<TexturesList, GESPMBuffer*> m_billboard_buffers;
irr::core::vector3df m_view_position;
btQuaternion m_billboard_rotation;
std::map<std::pair<GESPMBuffer*, TexturesList>, std::unordered_map<std::string,
std::vector<std::pair<irr::scene::ISceneNode*, int> > > >
m_visible_nodes;
std::map<std::pair<GESPMBuffer*, TexturesList>, irr::scene::IMesh*> m_mb_map;
std::map<std::string, std::vector<
std::pair<GEVulkanDynamicSPMBuffer*, irr::scene::ISceneNode*> > >
m_dynamic_spm_buffers;
GECullingTool* m_culling_tool;
GEVulkanLightHandler* m_light_handler;
std::vector<DrawCallData> m_cmds;
std::vector<ObjectData> m_visible_objects;
GEVulkanDynamicBuffer* m_dynamic_data;
GEVulkanDynamicBuffer* m_sbo_data;
const VkPhysicalDeviceLimits& m_limits;
size_t m_object_data_padded_size;
size_t m_skinning_data_padded_size;
size_t m_materials_padded_size;
size_t m_dynamic_spm_padded_size;
bool m_update_data_descriptor_sets;
VkDescriptorSetLayout m_data_layout;
VkDescriptorPool m_descriptor_pool;
std::vector<VkDescriptorSet> m_data_descriptor_sets;
VkPipelineLayout m_pipeline_layout, m_skybox_layout;
std::vector<VkPipelineLayout> m_deferred_layouts;
std::unordered_map<std::string, PipelineData> m_graphics_pipelines;
std::unordered_map<GEVulkanDynamicSPMBuffer*, std::pair<int, size_t> > m_dyspmb_materials;
GEVulkanSkyBoxRenderer* m_skybox_renderer;
GEVulkanTextureDescriptor* m_texture_descriptor;
std::unordered_set<GEVulkanAnimatedMeshSceneNode*> m_skinning_nodes;
std::unordered_map<std::string, std::pair<uint32_t, std::vector<int> > >
m_materials_data;
GEVulkanHiZDepth* m_hiz_depth;
// ------------------------------------------------------------------------
void createAllPipelines(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void createPipeline(GEVulkanDriver* vk, const PipelineSettings& settings,
std::unordered_map<std::string, std::shared_ptr<VkPipeline> >& dp_cache);
// ------------------------------------------------------------------------
void createVulkanData();
// ------------------------------------------------------------------------
std::string getShader(const irr::video::SMaterial& m);
// ------------------------------------------------------------------------
std::string getShader(irr::scene::ISceneNode* node, int material_id);
// ------------------------------------------------------------------------
bool bindPipeline(VkCommandBuffer cmd, const std::string& name,
VkPipeline* prev_pipeline,
GEVulkanPipelineType pt) const;
// ------------------------------------------------------------------------
TexturesList getTexturesList(const irr::video::SMaterial& m)
{
TexturesList textures;
for (unsigned i = 0; i < textures.size(); i++)
textures[i] = m.TextureLayer[i].Texture;
return textures;
}
// ------------------------------------------------------------------------
size_t getInitialSBOSize() const;
// ------------------------------------------------------------------------
void updateDataDescriptorSets(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void bindBaseVertex(GEVulkanDriver* vk, VkCommandBuffer cmd);
// ------------------------------------------------------------------------
std::string getDynamicBufferKey(const std::string& shader) const
{
char drawing_priority = (char)1;
auto it = m_graphics_pipelines.find(shader);
if (it != m_graphics_pipelines.end())
drawing_priority = it->second.m_settings.m_drawing_priority;
return std::string(1, drawing_priority) + shader;
}
// ------------------------------------------------------------------------
std::string getShaderFromKey(const std::string& key) const
{ return key.substr(1); }
// ------------------------------------------------------------------------
void bindSingleMaterial(VkCommandBuffer cmd,
const std::string& cur_pipeline,
int material_id, GEVulkanPipelineType pt);
// ------------------------------------------------------------------------
void bindDataDescriptor(VkCommandBuffer cmd, int current_buffer_idx,
std::vector<uint32_t>& dynamic_offsets)
{
vkCmdBindDescriptorSets(cmd,
VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 1, 1,
&m_data_descriptor_sets[current_buffer_idx],
dynamic_offsets.size(), dynamic_offsets.data());
}
// ------------------------------------------------------------------------
VertexDescription getDefaultVertexDescription() const;
// ------------------------------------------------------------------------
size_t getLightDataOffset() const;
// ------------------------------------------------------------------------
std::vector<uint32_t> getDefaultDynamicOffsets() const;
// ------------------------------------------------------------------------
VkRenderPass getRenderPassForPipelineCreation(GEVulkanDriver* vk,
GEVulkanPipelineType type);
// ------------------------------------------------------------------------
uint32_t getSubpassForPipelineCreation(GEVulkanDriver* vk,
GEVulkanPipelineType type);
public:
// ------------------------------------------------------------------------
GEVulkanDrawCall();
// ------------------------------------------------------------------------
~GEVulkanDrawCall();
// ------------------------------------------------------------------------
void addNode(irr::scene::ISceneNode* node);
// ------------------------------------------------------------------------
void addBillboardNode(irr::scene::ISceneNode* node,
irr::scene::ESCENE_NODE_TYPE node_type);
// ------------------------------------------------------------------------
void prepare(GEVulkanCameraSceneNode* cam);
// ------------------------------------------------------------------------
void generate(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void uploadDynamicData(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam,
VkCommandBuffer custom_cmd = VK_NULL_HANDLE);
// ------------------------------------------------------------------------
bool doDepthOnlyRenderingFirst();
// ------------------------------------------------------------------------
void bindAllMaterials(VkCommandBuffer cmd);
// ------------------------------------------------------------------------
void prepareRendering(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
void prepareViewport(GEVulkanDriver* vk, GEVulkanCameraSceneNode* cam,
VkCommandBuffer cmd);
// ------------------------------------------------------------------------
void renderPipeline(GEVulkanDriver* vk, VkCommandBuffer cmd,
GEVulkanPipelineType pt, bool& rebind_base_vertex);
// ------------------------------------------------------------------------
bool renderSkyBox(GEVulkanDriver* vk, VkCommandBuffer cmd);
// ------------------------------------------------------------------------
void renderDeferredLighting(GEVulkanDriver* vk, VkCommandBuffer cmd);
// ------------------------------------------------------------------------
void renderDeferredConvertColor(GEVulkanDriver* vk, VkCommandBuffer cmd);
// ------------------------------------------------------------------------
void renderDisplaceColor(GEVulkanDriver* vk, VkCommandBuffer cmd,
VkBool32 has_displace);
// ------------------------------------------------------------------------
unsigned getPolyCount() const
{
unsigned result = 0;
for (auto& cmd : m_cmds)
result += (cmd.m_cmd.indexCount / 3) * cmd.m_cmd.instanceCount;
return result;
}
// ------------------------------------------------------------------------
void reset()
{
m_visible_nodes.clear();
m_mb_map.clear();
m_cmds.clear();
m_visible_objects.clear();
m_dyspmb_materials.clear();
m_skinning_nodes.clear();
m_materials_data.clear();
m_dynamic_spm_buffers.clear();
m_skybox_renderer = NULL;
}
// ------------------------------------------------------------------------
void addSkyBox(irr::scene::ISceneNode* node);
// ------------------------------------------------------------------------
void addLightNode(irr::scene::ILightSceneNode* node);
// ------------------------------------------------------------------------
bool hasShaderForRendering(const std::string& shader)
{
const std::string& dbk = getDynamicBufferKey(shader);
if (m_dynamic_spm_buffers.find(dbk) != m_dynamic_spm_buffers.end())
return true;
return m_materials_data.find(shader) != m_materials_data.end();
}
// ------------------------------------------------------------------------
GEVulkanHiZDepth* getHiZDepth() const { return m_hiz_depth; }
}; // GEVulkanDrawCall
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,225 @@
#include "ge_vulkan_dynamic_buffer.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_main.hpp"
#include <array>
#include <vector>
#include <functional>
namespace GE
{
int GEVulkanDynamicBuffer::m_supports_host_transfer = -1;
// ----------------------------------------------------------------------------
GEVulkanDynamicBuffer::GEVulkanDynamicBuffer(VkBufferUsageFlags usage,
size_t initial_size,
unsigned host_buffer_size,
unsigned local_buffer_size,
bool enable_host_transfer)
: m_usage(usage),
m_enable_host_transfer(enable_host_transfer)
{
m_size = m_real_size = initial_size;
m_host_buffer.resize(host_buffer_size, VK_NULL_HANDLE);
m_host_memory.resize(host_buffer_size, VK_NULL_HANDLE);
m_mapped_addr.resize(host_buffer_size, VK_NULL_HANDLE);
m_local_buffer.resize(local_buffer_size, VK_NULL_HANDLE);
m_local_memory.resize(local_buffer_size, VK_NULL_HANDLE);
for (unsigned i = 0; i < m_host_buffer.size(); i++)
initHostBuffer(i, m_local_buffer.size() == 0);
for (unsigned i = 0; i < m_local_buffer.size(); i++)
initLocalBuffer(i);
} // GEVulkanDynamicBuffer
// ----------------------------------------------------------------------------
GEVulkanDynamicBuffer::~GEVulkanDynamicBuffer()
{
destroy();
} // ~GEVulkanDynamicBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::initHostBuffer(unsigned frame, bool with_transfer)
{
GEVulkanDriver* vk = getVKDriver();
start:
VkMemoryPropertyFlags prop = {};
VkBuffer host_buffer = VK_NULL_HANDLE;
VmaAllocation host_memory = VK_NULL_HANDLE;
VmaAllocationCreateInfo host_info = {};
host_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
host_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
host_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (((with_transfer && m_supports_host_transfer == 1) ||
(with_transfer && m_supports_host_transfer == -1)) &&
m_enable_host_transfer)
{
host_info.flags |=
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT;
host_info.usage = VMA_MEMORY_USAGE_AUTO;
}
if (!vk->createBuffer(m_size, m_usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
host_info, host_buffer, host_memory))
{
vmaDestroyBuffer(vk->getVmaAllocator(), host_buffer, host_memory);
return;
}
if (with_transfer && m_enable_host_transfer &&
m_supports_host_transfer == -1)
{
vmaGetAllocationMemoryProperties(vk->getVmaAllocator(), host_memory,
&prop);
if ((prop & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
m_supports_host_transfer = 0;
vmaDestroyBuffer(vk->getVmaAllocator(), host_buffer, host_memory);
goto start;
}
else
m_supports_host_transfer = 1;
}
VmaAllocationInfo info = {};
vmaGetAllocationInfo(vk->getVmaAllocator(), host_memory, &info);
m_host_buffer[frame] = host_buffer;
m_host_memory[frame] = host_memory;
m_mapped_addr[frame] = info.pMappedData;
} // initHostBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::initLocalBuffer(unsigned frame)
{
GEVulkanDriver* vk = getVKDriver();
VkBuffer local_buffer = VK_NULL_HANDLE;
VmaAllocation local_memory = VK_NULL_HANDLE;
VmaAllocationCreateInfo local_info = {};
local_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
VkBufferUsageFlags flags = m_usage;
if (!m_host_buffer.empty())
flags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
if (!vk->createBuffer(m_size, flags, local_info, local_buffer,
local_memory))
{
vmaDestroyBuffer(vk->getVmaAllocator(), local_buffer, local_memory);
return;
}
m_local_buffer[frame] = local_buffer;
m_local_memory[frame] = local_memory;
} // initLocalBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::destroy()
{
GEVulkanDriver* vk = getVKDriver();
vk->waitIdle();
for (unsigned i = 0; i < m_host_buffer.size(); i++)
{
vmaDestroyBuffer(vk->getVmaAllocator(), m_host_buffer[i],
m_host_memory[i]);
m_host_buffer[i] = VK_NULL_HANDLE;
m_host_memory[i] = VK_NULL_HANDLE;
m_mapped_addr[i] = NULL;
}
for (unsigned i = 0; i < m_local_buffer.size(); i++)
{
vmaDestroyBuffer(vk->getVmaAllocator(), m_local_buffer[i],
m_local_memory[i]);
m_local_buffer[i] = VK_NULL_HANDLE;
m_local_memory[i] = VK_NULL_HANDLE;
}
} // destroy
// ----------------------------------------------------------------------------
bool GEVulkanDynamicBuffer::setCurrentData(const std::vector<
std::pair<void*, size_t> >& data,
VkCommandBuffer custom_cmd,
unsigned cur_frame)
{
GEVulkanDriver* vk = getVKDriver();
size_t size = 0;
for (auto& p : data)
size += p.second;
bool ret = resizeIfNeeded(size);
m_real_size = size;
bool forced_frame = true;
if (cur_frame == (unsigned)-1)
{
cur_frame = vk->getCurrentFrame();
forced_frame = false;
}
if (cur_frame >= m_mapped_addr.size())
cur_frame = 0;
if (size == 0 || m_mapped_addr.empty() || m_mapped_addr[cur_frame] == NULL)
return ret;
uint8_t* addr = (uint8_t*)m_mapped_addr[cur_frame];
for (auto& p : data)
{
if (p.first != NULL)
memcpy(addr, p.first, p.second);
addr += p.second;
}
vmaFlushAllocation(vk->getVmaAllocator(), m_host_memory[cur_frame], 0,
size);
if (!m_local_buffer.empty())
{
unsigned cur_local_frame = vk->getCurrentFrame();
if (forced_frame)
cur_local_frame = cur_frame;
if (cur_local_frame >= m_local_buffer.size())
cur_local_frame = 0;
VkBufferCopy copy_region = {};
copy_region.size = size;
vkCmdCopyBuffer(custom_cmd ? custom_cmd : vk->getCurrentCommandBuffer(),
m_host_buffer[cur_frame], m_local_buffer[cur_local_frame], 1,
&copy_region);
}
return ret;
} // setCurrentData
// ----------------------------------------------------------------------------
bool GEVulkanDynamicBuffer::resizeIfNeeded(size_t new_size)
{
if (new_size > m_size)
{
destroy();
m_size = new_size + 100;
for (unsigned i = 0; i < m_host_buffer.size(); i++)
initHostBuffer(i, m_local_buffer.size() == 0);
for (unsigned i = 0; i < m_local_buffer.size(); i++)
initLocalBuffer(i);
return true;
}
return false;
} // resizeIfNeeded
// ----------------------------------------------------------------------------
VkBuffer GEVulkanDynamicBuffer::getCurrentBuffer() const
{
unsigned cur_frame = getVKDriver()->getCurrentFrame();
if (m_local_buffer.empty())
{
if (cur_frame >= m_host_buffer.size())
cur_frame = 0;
return m_host_buffer[cur_frame];
}
else
{
if (cur_frame >= m_local_buffer.size())
cur_frame = 0;
return m_local_buffer[cur_frame];
}
} // getCurrentBuffer
}
@@ -0,0 +1,81 @@
#ifndef HEADER_GE_VULKAN_DYNAMIC_BUFFER_HPP
#define HEADER_GE_VULKAN_DYNAMIC_BUFFER_HPP
#include "vulkan_wrapper.h"
#include "ge_vma.hpp"
#include <vector>
#include <utility>
namespace GE
{
class GEVulkanDynamicBuffer
{
private:
std::vector<VkBuffer> m_host_buffer, m_local_buffer;
std::vector<VmaAllocation> m_host_memory, m_local_memory;
std::vector<void*> m_mapped_addr;
size_t m_size, m_real_size;
const VkBufferUsageFlags m_usage;
const bool m_enable_host_transfer;
static int m_supports_host_transfer;
// ------------------------------------------------------------------------
void initHostBuffer(unsigned frame, bool with_transfer);
// ------------------------------------------------------------------------
void initLocalBuffer(unsigned frame);
// ------------------------------------------------------------------------
void destroy();
public:
// ------------------------------------------------------------------------
GEVulkanDynamicBuffer(VkBufferUsageFlags usage, size_t initial_size,
unsigned host_buffer_size,
unsigned local_buffer_size,
bool enable_host_transfer = true);
// ------------------------------------------------------------------------
~GEVulkanDynamicBuffer();
// ------------------------------------------------------------------------
bool setCurrentData(const std::vector<std::pair<void*, size_t> >& data,
VkCommandBuffer custom_cmd = VK_NULL_HANDLE,
unsigned cur_frame = -1);
// ------------------------------------------------------------------------
bool setCurrentData(void* data, size_t size,
VkCommandBuffer custom_cmd = VK_NULL_HANDLE,
unsigned cur_frame = -1)
{ return setCurrentData({{ data, size }}, custom_cmd, cur_frame); }
// ------------------------------------------------------------------------
VkBuffer getCurrentBuffer() const;
// ------------------------------------------------------------------------
bool resizeIfNeeded(size_t new_size);
// ------------------------------------------------------------------------
size_t getSize() const { return m_size; }
// ------------------------------------------------------------------------
size_t getRealSize() const { return m_real_size; }
// ------------------------------------------------------------------------
std::vector<VkBuffer>& getHostBuffer() { return m_host_buffer; }
// ------------------------------------------------------------------------
std::vector<VkBuffer>& getLocalBuffer() { return m_local_buffer; }
// ------------------------------------------------------------------------
std::vector<VmaAllocation>& getHostMemory() { return m_host_memory; }
// ------------------------------------------------------------------------
std::vector<VmaAllocation>& getLocalMemory() { return m_local_memory; }
// ------------------------------------------------------------------------
std::vector<void*>& getMappedAddr() { return m_mapped_addr; }
// ------------------------------------------------------------------------
/** This can only be called after creating an instance with
* local_buffer_size == 0 first, which is always done in
* GEVulkan2dRenderer::createTrisBuffers. */
static bool supportsHostTransfer()
{ return m_supports_host_transfer == 1; }
}; // GEVulkanDynamicBuffer
}
#endif
@@ -0,0 +1,129 @@
#include "ge_vulkan_dynamic_spm_buffer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_dynamic_buffer.hpp"
#include <cmath>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanDynamicSPMBuffer::GEVulkanDynamicSPMBuffer()
{
unsigned frame_count = GEVulkanDriver::getMaxFrameInFlight() + 1;
m_vertex_buffer = new GEVulkanDynamicBuffer(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 100, frame_count, 0);
m_index_buffer = new GEVulkanDynamicBuffer(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 100, frame_count, 0);
m_vk = getVKDriver();
m_vk->addDynamicSPMBuffer(this);
m_vertex_update_offsets = new uint32_t[frame_count]();
m_index_update_offsets = new uint32_t[frame_count]();
} // GEVulkanDynamicSPMBuffer
// ----------------------------------------------------------------------------
GEVulkanDynamicSPMBuffer::~GEVulkanDynamicSPMBuffer()
{
m_vk->removeDynamicSPMBuffer(this);
delete m_vertex_buffer;
delete m_index_buffer;
delete [] m_vertex_update_offsets;
delete [] m_index_update_offsets;
} // ~GEVulkanDynamicSPMBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicSPMBuffer::updateVertexIndexBuffer(int buffer_index)
{
const size_t stride = sizeof(irr::video::S3DVertexSkinnedMesh) - 16;
const unsigned frame_count = GEVulkanDriver::getMaxFrameInFlight() + 1;
if (m_vertex_update_offsets[buffer_index] != m_vertices.size())
{
double vertex_size = (double)(m_vertices.size() * stride);
double base = std::log2(vertex_size);
if (m_vertex_buffer->resizeIfNeeded(2 << (unsigned)base))
std::fill_n(m_vertex_update_offsets, frame_count, 0);
uint8_t* mapped_addr = (uint8_t*)m_vertex_buffer->getMappedAddr()
[buffer_index];
mapped_addr += m_vertex_update_offsets[buffer_index] * stride;
copyToMappedBuffer((uint32_t*)mapped_addr, this,
m_vertex_update_offsets[buffer_index]);
m_vertex_update_offsets[buffer_index] = m_vertices.size();
}
if (m_index_update_offsets[buffer_index] != m_indices.size())
{
double index_size = (double)(m_indices.size() * sizeof(uint16_t));
double base = std::log2(index_size);
if (m_index_buffer->resizeIfNeeded(2 << (unsigned)base))
std::fill_n(m_index_update_offsets, frame_count, 0);
uint8_t* mapped_addr = (uint8_t*)m_index_buffer->getMappedAddr()
[buffer_index];
unsigned ioffset = m_index_update_offsets[buffer_index] * sizeof(uint16_t);
mapped_addr += ioffset;
for (unsigned i = m_index_update_offsets[buffer_index];
i < m_indices.size(); i++)
{
memcpy(mapped_addr, &m_indices[i], sizeof(uint16_t));
mapped_addr += sizeof(uint16_t);
}
m_index_update_offsets[buffer_index] = m_indices.size();
}
} // updateVertexIndexBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicSPMBuffer::drawDynamicVertexIndexBuffer(VkCommandBuffer cmd,
int buffer_index)
{
std::array<VkBuffer, 2> vertex_buffer =
{{
m_vertex_buffer->getHostBuffer()[buffer_index],
m_vertex_buffer->getHostBuffer()[buffer_index]
}};
std::array<VkDeviceSize, 2> offsets =
{{
0,
0
}};
vkCmdBindVertexBuffers(cmd, 0, vertex_buffer.size(), vertex_buffer.data(),
offsets.data());
VkBuffer index_buffer = m_index_buffer->getHostBuffer()[buffer_index];
vkCmdBindIndexBuffer(cmd, index_buffer, 0, VK_INDEX_TYPE_UINT16);
vkCmdDrawIndexed(cmd, getIndexCount(), 1, 0, 0, 0);
} // drawDynamicVertexIndexBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicSPMBuffer::setDirtyOffset(irr::u32 offset,
irr::scene::E_BUFFER_TYPE buffer)
{
int vertex_update_offset = -1;
int index_update_offset = -1;
if (buffer == irr::scene::EBT_VERTEX_AND_INDEX)
vertex_update_offset = index_update_offset = offset;
else if (buffer == irr::scene::EBT_VERTEX)
vertex_update_offset = offset;
else if (buffer == irr::scene::EBT_INDEX)
index_update_offset = offset;
unsigned frame_count = GEVulkanDriver::getMaxFrameInFlight() + 1;
if (vertex_update_offset != -1)
{
for (unsigned i = 0; i < frame_count; i++)
{
if (m_vertex_update_offsets[i] > vertex_update_offset)
m_vertex_update_offsets[i] = vertex_update_offset;
}
}
if (index_update_offset != -1)
{
for (unsigned i = 0; i < frame_count; i++)
{
if (m_index_update_offsets[i] > index_update_offset)
m_index_update_offsets[i] = index_update_offset;
}
}
} // setDirtyOffset
} // end namespace GE
@@ -0,0 +1,334 @@
#include "ge_vulkan_environment_map.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_array_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include "ge_vulkan_skybox_renderer.hpp"
#include <algorithm>
#include <array>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanEnvironmentMap::GEVulkanEnvironmentMap(GEVulkanSkyBoxRenderer* skybox)
: m_skybox(skybox)
{
m_skybox->m_env_cubemap_loading.store(true);
} // GEVulkanEnvironmentMap
// ----------------------------------------------------------------------------
GEVulkanEnvironmentMap::~GEVulkanEnvironmentMap()
{
m_skybox->m_env_cubemap_loading.store(false);
} // ~GEVulkanEnvironmentMap
// ----------------------------------------------------------------------------
void GEVulkanEnvironmentMap::load()
{
struct PushConstants
{
int m_size;
int m_sample_count;
int m_level;
int m_total_mipmaps;
};
PushConstants pc;
VkPushConstantRange push_constant = {};
push_constant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
push_constant.offset = 0;
push_constant.size = sizeof(PushConstants);
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 1;
pipeline_layout_info.pushConstantRangeCount = 1;
pipeline_layout_info.pPushConstantRanges = &push_constant;
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {};
bindings[0].binding = 0;
bindings[0].descriptorCount = 1;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
bindings[1].binding = 1;
bindings[1].descriptorCount = 1;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = bindings.data();
setinfo.bindingCount = bindings.size();
GEVulkanDriver* vk = getVKDriver();
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
std::vector<VkDescriptorSet> descriptor_sets;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipeline diffuse_pipeline = VK_NULL_HANDLE;
VkPipeline specular_pipeline = VK_NULL_HANDLE;
auto levels = [](GEVulkanArrayTexture* t)
{
std::vector<unsigned> l;
l.push_back(t->getSize().Width);
unsigned width = l.back();
while (true)
{
width = width < 2 ? 1 : width >> 1;
l.push_back(width);
if (width == 1)
break;
}
return l;
};
GEVulkanArrayTexture* texture_cubemap = m_skybox->m_texture_cubemap;
GEVulkanArrayTexture* diffuse_env_cubemap =
m_skybox->m_diffuse_env_cubemap;
GEVulkanArrayTexture* specular_env_cubemap =
m_skybox->m_specular_env_cubemap;
std::vector<unsigned> diffuse_levels = levels(diffuse_env_cubemap);
std::vector<unsigned> specular_levels = levels(specular_env_cubemap);
unsigned total_levels = diffuse_levels.size() + specular_levels.size();
std::array<VkDescriptorPoolSize, 2> pool_sizes =
{{
{
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, total_levels
},
{
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, total_levels
}
}};
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.maxSets = total_levels;
pool_info.poolSizeCount = pool_sizes.size();
pool_info.pPoolSizes = pool_sizes.data();
std::vector<VkDescriptorSetLayout> data_layouts;
VkDescriptorSetAllocateInfo alloc_info = {};
VkComputePipelineCreateInfo compute_info = {};
compute_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
compute_info.stage.sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
compute_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
compute_info.stage.pName = "main";
VkCommandBuffer cmd = VK_NULL_HANDLE;
std::vector<VkDescriptorImageInfo> image_infos;
std::vector<VkImageView> image_views;
for (unsigned i = 0; i < diffuse_levels.size(); i++)
{
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = diffuse_env_cubemap->getImage();
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
view_info.format = diffuse_env_cubemap->getInternalFormat();
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = i;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 6;
VkImageView view = VK_NULL_HANDLE;
if (vkCreateImageView(vk->getDevice(), &view_info, NULL, &view)
!= VK_SUCCESS)
{
printf("vkCreateImageView failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
image_views.push_back(view);
}
for (unsigned i = 0; i < specular_levels.size(); i++)
{
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = specular_env_cubemap->getImage();
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
view_info.format = specular_env_cubemap->getInternalFormat();
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = i;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 6;
VkImageView view = VK_NULL_HANDLE;
if (vkCreateImageView(vk->getDevice(), &view_info, NULL, &view)
!= VK_SUCCESS)
{
printf("vkCreateImageView failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
image_views.push_back(view);
}
if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo, NULL,
&layout) != VK_SUCCESS)
{
printf("vkCreateDescriptorSetLayout failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL,
&descriptor_pool) != VK_SUCCESS)
{
printf("createDescriptorPool for GEVulkanEnvironmentMap::load");
goto destroy;
}
data_layouts.resize(total_levels, layout);
descriptor_sets.resize(total_levels);
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = descriptor_pool;
alloc_info.descriptorSetCount = data_layouts.size();
alloc_info.pSetLayouts = data_layouts.data();
if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info,
descriptor_sets.data()) != VK_SUCCESS)
{
printf("vkAllocateDescriptorSets failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
for (VkImageView view : image_views)
{
VkDescriptorImageInfo info = {};
info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info.sampler = vk->getSampler(GVS_SKYBOX);
info.imageView = texture_cubemap->getImageView(true/*srgb*/)->load();
image_infos.push_back(info);
info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
info.imageView = view;
image_infos.push_back(info);
}
for (unsigned i = 0; i < total_levels; i++)
{
std::array<VkWriteDescriptorSet, 2> write_descriptor_set = {};
write_descriptor_set[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set[0].dstSet = descriptor_sets[i];
write_descriptor_set[0].dstBinding = 0;
write_descriptor_set[0].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set[0].descriptorCount = 1;
write_descriptor_set[0].pImageInfo = &image_infos[i * 2];
write_descriptor_set[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set[1].dstSet = descriptor_sets[i];
write_descriptor_set[1].dstBinding = 1;
write_descriptor_set[1].descriptorType =
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
write_descriptor_set[1].descriptorCount = 1;
write_descriptor_set[1].pImageInfo = &image_infos[i * 2 + 1];
vkUpdateDescriptorSets(vk->getDevice(), write_descriptor_set.size(),
write_descriptor_set.data(), 0, NULL);
}
pipeline_layout_info.pSetLayouts = &layout;
if (vkCreatePipelineLayout(vk->getDevice(), &pipeline_layout_info, NULL,
&pipeline_layout) != VK_SUCCESS)
{
printf("vkCreatePipelineLayout failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
compute_info.stage.module =
GEVulkanShaderManager::getShader("diffuse_irradiance.comp");
compute_info.layout = pipeline_layout;
if (vkCreateComputePipelines(vk->getDevice(), VK_NULL_HANDLE, 1,
&compute_info, NULL, &diffuse_pipeline) != VK_SUCCESS)
{
printf("vkCreateComputePipelines failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
compute_info.stage.module =
GEVulkanShaderManager::getShader("specular_prefilter.comp");
if (vkCreateComputePipelines(vk->getDevice(), VK_NULL_HANDLE, 1,
&compute_info, NULL, &specular_pipeline) != VK_SUCCESS)
{
printf("vkCreateComputePipelines failed for "
"GEVulkanEnvironmentMap::load");
goto destroy;
}
cmd = GEVulkanCommandLoader::beginSingleTimeCommands();
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, diffuse_pipeline);
for (unsigned i = 0; i < diffuse_levels.size(); i++)
{
PushConstants pc;
pc.m_size = diffuse_levels[i];
pc.m_sample_count = getDiffuseEnvironmentMapSampleCount();
pc.m_level = i;
pc.m_total_mipmaps = diffuse_levels.size();
vkCmdPushConstants(cmd, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(PushConstants), &pc);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
pipeline_layout, 0, 1, &descriptor_sets[i], 0, NULL);
// Calculate dispatch size (ceil(size/16))
uint32_t dispatch_size = (pc.m_size + 15) / 16;
vkCmdDispatch(cmd, dispatch_size, dispatch_size, 6);
}
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, specular_pipeline);
for (unsigned i = 0; i < specular_levels.size(); i++)
{
const unsigned offset = diffuse_levels.size();
auto next_power_of_2 = [](unsigned v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
};
PushConstants pc;
pc.m_size = specular_levels[i];
// Calculate sample count: start with size / 2, minimum 16
pc.m_sample_count = std::max(16u,
next_power_of_2(specular_levels[i] / 2));
pc.m_level = i;
pc.m_total_mipmaps = specular_levels.size();
vkCmdPushConstants(cmd, pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(PushConstants), &pc);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
pipeline_layout, 0, 1, &descriptor_sets[i + offset], 0, NULL);
uint32_t dispatch_size = (pc.m_size + 15) / 16;
vkCmdDispatch(cmd, dispatch_size, dispatch_size, 6);
}
destroy:
if (cmd != VK_NULL_HANDLE)
GEVulkanCommandLoader::endSingleTimeCommands(cmd);
if (specular_pipeline != VK_NULL_HANDLE)
vkDestroyPipeline(vk->getDevice(), specular_pipeline, NULL);
if (diffuse_pipeline != VK_NULL_HANDLE)
vkDestroyPipeline(vk->getDevice(), diffuse_pipeline, NULL);
if (pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(vk->getDevice(), pipeline_layout, NULL);
if (descriptor_pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(vk->getDevice(), descriptor_pool, NULL);
if (layout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(vk->getDevice(), layout, NULL);
for (VkImageView view : image_views)
vkDestroyImageView(vk->getDevice(), view, NULL);
} // load
}
@@ -0,0 +1,37 @@
#ifndef HEADER_GE_VULKAN_ENVIRONMENT_MAP_HPP
#define HEADER_GE_VULKAN_ENVIRONMENT_MAP_HPP
#include "dimension2d.h"
namespace GE
{
class GEVulkanSkyBoxRenderer;
class GEVulkanEnvironmentMap
{
private:
GEVulkanSkyBoxRenderer* m_skybox;
public:
// ------------------------------------------------------------------------
GEVulkanEnvironmentMap(GEVulkanSkyBoxRenderer* skybox);
// ------------------------------------------------------------------------
~GEVulkanEnvironmentMap();
// ------------------------------------------------------------------------
/** A much lower resolution than the input cubemap is sufficient because
* diffuse reflections are inherently blurry.
*/
const static irr::core::dimension2du getDiffuseEnvironmentMapSize()
{ return irr::core::dimension2du(32, 32); }
// ------------------------------------------------------------------------
const static irr::core::dimension2du getSpecularEnvironmentMapSize()
{ return irr::core::dimension2du(256, 256); }
// ------------------------------------------------------------------------
const static unsigned getDiffuseEnvironmentMapSampleCount()
{ return 256; }
// ------------------------------------------------------------------------
void load();
}; // GEVulkanEnvironmentMap
}
#endif
@@ -0,0 +1,154 @@
#include "ge_vulkan_fbo_texture.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_attachment_texture.hpp"
#include "ge_vulkan_driver.hpp"
#include <array>
#include <exception>
#include <stdexcept>
namespace GE
{
GEVulkanFBOTexture::GEVulkanFBOTexture(GEVulkanDriver* vk,
const core::dimension2d<u32>& size,
bool lazy_depth)
: GEVulkanTexture()
{
m_vk = vk;
m_vulkan_device = m_vk->getDevice();
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_has_mipmaps = false;
m_locked_data = NULL;
m_size = m_orig_size = size;
m_internal_format = VK_FORMAT_B8G8R8A8_UNORM;
m_depth_texture = GEVulkanAttachmentTexture::createDepthTexture(
m_vk, size, lazy_depth);
} // GEVulkanFBOTexture
// ----------------------------------------------------------------------------
GEVulkanFBOTexture::~GEVulkanFBOTexture()
{
delete m_depth_texture;
clearVulkanData();
m_vk->handleDeletedTextures();
for (VkFramebuffer fb : m_rtt_frame_buffer)
{
if (fb != VK_NULL_HANDLE)
vkDestroyFramebuffer(m_vk->getDevice(), fb, NULL);
}
for (VkRenderPass rp : m_rtt_render_pass)
{
if (rp != VK_NULL_HANDLE)
vkDestroyRenderPass(m_vk->getDevice(), rp, NULL);
}
} // ~GEVulkanFBOTexture
// ----------------------------------------------------------------------------
void GEVulkanFBOTexture::createRTT()
{
createOutputImage();
std::array<VkAttachmentDescription, 2> attchment_desc = {};
// Color attachment
attchment_desc[0].format = m_internal_format;
attchment_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
attchment_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attchment_desc[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attchment_desc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attchment_desc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchment_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attchment_desc[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
// Depth attachment
attchment_desc[1].format = m_depth_texture->getInternalFormat();
attchment_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
attchment_desc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attchment_desc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchment_desc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attchment_desc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attchment_desc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attchment_desc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference color_reference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
VkAttachmentReference depth_reference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
VkSubpassDescription subpass_desc = {};
subpass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass_desc.colorAttachmentCount = 1;
subpass_desc.pColorAttachments = &color_reference;
subpass_desc.pDepthStencilAttachment = &depth_reference;
// Use subpass dependencies for layout transitions
std::array<VkSubpassDependency, 2> dependencies;
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
// Create the actual render pass
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = attchment_desc.size();
render_pass_info.pAttachments = attchment_desc.data();
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass_desc;
render_pass_info.dependencyCount = dependencies.size();
render_pass_info.pDependencies = dependencies.data();
m_rtt_render_pass.resize(1);
if (vkCreateRenderPass(m_vk->getDevice(), &render_pass_info, NULL,
&m_rtt_render_pass[0]) != VK_SUCCESS)
throw std::runtime_error("vkCreateRenderPass failed in createFBOdata");
std::array<VkImageView, 2> attachments =
{{
*(m_image_view.get()),
(VkImageView)m_depth_texture->getTextureHandler(),
}};
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = m_rtt_render_pass[0];
framebuffer_info.attachmentCount = attachments.size();
framebuffer_info.pAttachments = attachments.data();
framebuffer_info.width = m_size.Width;
framebuffer_info.height = m_size.Height;
framebuffer_info.layers = 1;
m_rtt_frame_buffer.resize(1);
if (vkCreateFramebuffer(m_vk->getDevice(), &framebuffer_info,
NULL, &m_rtt_frame_buffer[0]) != VK_SUCCESS)
throw std::runtime_error("vkCreateFramebuffer failed in createFBOdata");
} // createRTT
// ----------------------------------------------------------------------------
void GEVulkanFBOTexture::createOutputImage(VkImageUsageFlags usage)
{
if (!createImage(usage))
throw std::runtime_error("createImage failed for fbo texture");
if (!createImageView(VK_IMAGE_ASPECT_COLOR_BIT, false/*create_srgb_view*/))
throw std::runtime_error("createImageView failed for fbo texture");
} // createOutputImage
}
@@ -0,0 +1,85 @@
#ifndef HEADER_GE_VULKAN_FBO_TEXTURE_HPP
#define HEADER_GE_VULKAN_FBO_TEXTURE_HPP
#include "ge_vulkan_texture.hpp"
namespace GE
{
class GEVulkanAttachmentTexture;
class GEVulkanFBOTexture : public GEVulkanTexture
{
protected:
GEVulkanAttachmentTexture* m_depth_texture;
std::vector<VkRenderPass> m_rtt_render_pass;
std::vector<VkFramebuffer> m_rtt_frame_buffer;
public:
// ------------------------------------------------------------------------
GEVulkanFBOTexture(GEVulkanDriver* vk, const core::dimension2d<u32>& size,
bool lazy_depth = true);
// ------------------------------------------------------------------------
virtual ~GEVulkanFBOTexture();
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
{ return NULL; }
// ------------------------------------------------------------------------
virtual void unlock() {}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{ return m_orig_size; }
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return false; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{ return (u64)(m_image_view.get()->load()); }
// ------------------------------------------------------------------------
virtual void reload() {}
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y) {}
// ------------------------------------------------------------------------
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView(
bool srgb = false) const
{ return m_image_view; }
// ------------------------------------------------------------------------
virtual void createRTT();
// ------------------------------------------------------------------------
virtual void createOutputImage(VkImageUsageFlags usage =
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
// ------------------------------------------------------------------------
VkRenderPass getRTTRenderPass(unsigned id = 0) const
{ return m_rtt_render_pass.at(id); }
// ------------------------------------------------------------------------
unsigned getRTTRenderPassCount() const
{ return m_rtt_render_pass.size(); }
// ------------------------------------------------------------------------
VkFramebuffer getRTTFramebuffer(unsigned id = 0) const
{ return m_rtt_frame_buffer.at(id); }
// ------------------------------------------------------------------------
GEVulkanAttachmentTexture* getDepthTexture() const
{ return m_depth_texture; }
// ------------------------------------------------------------------------
virtual bool isDeferredFBO() const { return false; }
// ------------------------------------------------------------------------
virtual bool useSwapChainOutput() const { return false; }
// ------------------------------------------------------------------------
virtual unsigned getZeroClearCountForPass(unsigned pass) const
{ return 0; }
// ------------------------------------------------------------------------
virtual VkDescriptorSetLayout getDescriptorSetLayout(unsigned id) const
{ return VK_NULL_HANDLE; }
// ------------------------------------------------------------------------
virtual const VkDescriptorSet* getDescriptorSet(unsigned id) const
{ return NULL; }
}; // GEVulkanFBOTexture
}
#endif
@@ -0,0 +1,380 @@
#include "ge_vulkan_features.hpp"
#include "ge_compressor_astc_4x4.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include <algorithm>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>
#include "../source/Irrlicht/os.h"
#include <SDL_cpuinfo.h>
extern "C" const char* Android_Custom_Vulkan_Driver_In_Use();
namespace GE
{
namespace GEVulkanFeatures
{
// ============================================================================
bool g_supports_bind_textures_at_once = false;
bool g_supports_rgba8_blit = false;
bool g_supports_r8_blit = false;
// https://chunkstories.xyz/blog/a-note-on-descriptor-indexing
bool g_supports_descriptor_indexing = false;
bool g_supports_non_uniform_indexing = false;
bool g_supports_partially_bound = false;
uint32_t g_max_sampler_supported = 0;
bool g_supports_multi_draw_indirect = false;
bool g_supports_base_vertex_rendering = true;
bool g_supports_compute_in_main_queue = false;
bool g_supports_shader_draw_parameters = false;
bool g_supports_s3tc_bc3 = false;
bool g_supports_bptc_bc7 = false;
bool g_supports_astc_4x4 = false;
bool g_supports_shader_storage_image_extended_format = false;
} // GEVulkanFeatures
// ============================================================================
void GEVulkanFeatures::init(GEVulkanDriver* vk)
{
g_supports_bind_textures_at_once = true;
bool dynamic_indexing = true;
VkPhysicalDeviceLimits limit = vk->getPhysicalDeviceProperties().limits;
// https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxDescriptorSetSamplers&platform=all
// https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxDescriptorSetSampledImages&platform=all
// https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPerStageDescriptorSamplers&platform=all
// https://vulkan.gpuinfo.org/displaydevicelimit.php?name=maxPerStageDescriptorSampledImages&platform=all
// We decide 512 (GEVulkanShaderManager::getSamplerSize()) based on those infos
g_max_sampler_supported = std::min(
{
limit.maxDescriptorSetSamplers,
limit.maxDescriptorSetSampledImages,
limit.maxPerStageDescriptorSamplers,
limit.maxPerStageDescriptorSampledImages
});
const unsigned max_sampler_size = GEVulkanShaderManager::getSamplerSize();
if (max_sampler_size > g_max_sampler_supported)
g_supports_bind_textures_at_once = false;
if (vk->getPhysicalDeviceFeatures().shaderSampledImageArrayDynamicIndexing == VK_FALSE)
{
dynamic_indexing = false;
g_supports_bind_textures_at_once = false;
}
g_supports_multi_draw_indirect = vk->getPhysicalDeviceFeatures().multiDrawIndirect &&
vk->getPhysicalDeviceFeatures().drawIndirectFirstInstance;
VkFormatProperties format_properties = {};
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_R8G8B8A8_UNORM, &format_properties);
g_supports_rgba8_blit = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
format_properties = {};
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_R8_UNORM, &format_properties);
g_supports_r8_blit = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
format_properties = {};
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_BC3_UNORM_BLOCK, &format_properties);
g_supports_s3tc_bc3 = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
#ifdef BC7_ISPC
format_properties = {};
// We compile bc7e.ispc with avx2 on
if (SDL_HasAVX2() == SDL_TRUE)
{
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_BC7_UNORM_BLOCK, &format_properties);
g_supports_bptc_bc7 = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
}
#endif
format_properties = {};
vkGetPhysicalDeviceFormatProperties(vk->getPhysicalDevice(),
VK_FORMAT_ASTC_4x4_UNORM_BLOCK, &format_properties);
g_supports_astc_4x4 = format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
g_supports_shader_storage_image_extended_format = vk->getPhysicalDeviceFeatures().shaderStorageImageExtendedFormats;
if (g_supports_shader_storage_image_extended_format)
{
// iOS simulator doesn't support writing to VK_FORMAT_A2B10G10R10_UNORM_PACK32
try
{
std::vector<VkFormat> a2b10g10r10 =
{
VK_FORMAT_A2B10G10R10_UNORM_PACK32,
};
VkFormat format = vk->findSupportedFormat(a2b10g10r10,
VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT |
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
}
catch (std::runtime_error& e)
{
g_supports_shader_storage_image_extended_format = false;
}
}
uint32_t extension_count;
vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL,
&extension_count, NULL);
std::vector<VkExtensionProperties> extensions(extension_count);
vkEnumerateDeviceExtensionProperties(vk->getPhysicalDevice(), NULL,
&extension_count, &extensions[0]);
for (VkExtensionProperties& prop : extensions)
{
if (strcmp(prop.extensionName,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME) == 0)
g_supports_descriptor_indexing = true;
}
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(vk->getPhysicalDevice(),
&queue_family_count, NULL);
if (queue_family_count != 0)
{
std::vector<VkQueueFamilyProperties> queue_families(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(vk->getPhysicalDevice(),
&queue_family_count, &queue_families[0]);
uint32_t main_family = vk->getGraphicsFamily();
if (main_family < queue_families.size())
{
g_supports_compute_in_main_queue =
(queue_families[main_family].queueFlags & VK_QUEUE_COMPUTE_BIT)
!= 0;
}
}
VkPhysicalDeviceFeatures2 supported_features = {};
supported_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features = {};
descriptor_indexing_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;
supported_features.pNext = &descriptor_indexing_features;
VkPhysicalDeviceShaderDrawParametersFeatures shader_draw = {};
shader_draw.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
descriptor_indexing_features.pNext = &shader_draw;
PFN_vkGetPhysicalDeviceFeatures2 get_features = vkGetPhysicalDeviceFeatures2;
if (vk->getPhysicalDeviceProperties().apiVersion < VK_API_VERSION_1_1 ||
!get_features)
{
get_features = (PFN_vkGetPhysicalDeviceFeatures2)
vkGetPhysicalDeviceFeatures2KHR;
}
if (!get_features)
return;
get_features(vk->getPhysicalDevice(), &supported_features);
if (supported_features.sType !=
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2)
return;
g_supports_non_uniform_indexing = (descriptor_indexing_features
.shaderSampledImageArrayNonUniformIndexing == VK_TRUE);
g_supports_partially_bound = (descriptor_indexing_features
.descriptorBindingPartiallyBound == VK_TRUE);
g_supports_shader_draw_parameters = (shader_draw
.shaderDrawParameters == VK_TRUE);
#if defined(__APPLE__)
bool missing_vkGetPhysicalDeviceProperties2 =
!vkGetPhysicalDeviceProperties2;
if (!missing_vkGetPhysicalDeviceProperties2 &&
!g_supports_bind_textures_at_once && dynamic_indexing)
{
// Required for moltenvk argument buffers
VkPhysicalDeviceProperties2 props1 = {};
props1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
VkPhysicalDeviceDescriptorIndexingProperties props2 = {};
props2.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES;
props1.pNext = &props2;
vkGetPhysicalDeviceProperties2(vk->getPhysicalDevice(), &props1);
if (props2.sType ==
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES)
{
g_max_sampler_supported = std::min(
{
props2.maxPerStageDescriptorUpdateAfterBindSamplers,
props2.maxPerStageDescriptorUpdateAfterBindSampledImages,
props2.maxDescriptorSetUpdateAfterBindSamplers,
props2.maxDescriptorSetUpdateAfterBindSampledImages
});
g_supports_bind_textures_at_once =
g_max_sampler_supported >= max_sampler_size;
}
}
MVKPhysicalDeviceMetalFeatures mvk_features = {};
size_t mvk_features_size = sizeof(MVKPhysicalDeviceMetalFeatures);
vkGetPhysicalDeviceMetalFeaturesMVK(vk->getPhysicalDevice(), &mvk_features,
&mvk_features_size);
g_supports_base_vertex_rendering = mvk_features.baseVertexInstanceDrawing;
if (!g_supports_base_vertex_rendering)
g_supports_multi_draw_indirect = false;
// https://github.com/KhronosGroup/MoltenVK/issues/1743
g_supports_shader_draw_parameters = false;
#endif
#if defined(__ANDROID__) && defined(__aarch64__)
// Freedreno is slow with bindless code
if (Android_Custom_Vulkan_Driver_In_Use() != NULL)
g_supports_multi_draw_indirect = false;
#endif
} // init
// ----------------------------------------------------------------------------
void GEVulkanFeatures::printStats()
{
os::Printer::log(
"Vulkan can bind textures at once in shader",
g_supports_bind_textures_at_once ? "true" : "false");
os::Printer::log(
"Vulkan can bind mesh textures at once in shader",
supportsBindMeshTexturesAtOnce() ? "true" : "false");
os::Printer::log(
"Vulkan supports linear blitting for rgba8",
g_supports_rgba8_blit ? "true" : "false");
os::Printer::log(
"Vulkan supports linear blitting for r8",
g_supports_r8_blit ? "true" : "false");
os::Printer::log(
"Vulkan supports VK_EXT_descriptor_indexing",
g_supports_descriptor_indexing ? "true" : "false");
os::Printer::log(
"Vulkan supports multi-draw indirect",
g_supports_multi_draw_indirect ? "true" : "false");
os::Printer::log(
"Vulkan supports base vertex rendering",
g_supports_base_vertex_rendering ? "true" : "false");
os::Printer::log(
"Vulkan supports compute in main queue",
g_supports_compute_in_main_queue ? "true" : "false");
os::Printer::log(
"Vulkan supports shader draw parameters",
g_supports_shader_draw_parameters ? "true" : "false");
os::Printer::log(
"Vulkan supports s3 texture compression (bc3, dxt5)",
g_supports_s3tc_bc3 ? "true" : "false");
os::Printer::log(
"Vulkan supports BPTC texture compression (bc7)",
g_supports_bptc_bc7 ? "true" : "false");
os::Printer::log(
"Vulkan supports adaptive scalable texture compression (4x4 block)",
supportsASTC4x4() ? "true" : "false");
os::Printer::log("Vulkan supports shader storage image extended formats",
supportsShaderStorageImageExtendedFormats() ? "true" : "false");
os::Printer::log(
"Vulkan descriptor indexes can be dynamically non-uniform",
g_supports_non_uniform_indexing ? "true" : "false");
os::Printer::log(
"Vulkan descriptor can be partially bound",
g_supports_partially_bound ? "true" : "false");
} // printStats
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsBindTexturesAtOnce()
{
return g_supports_bind_textures_at_once;
} // supportsBindTexturesAtOnce
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsRGBA8Blit()
{
return g_supports_rgba8_blit;
} // supportsRGBA8Blit
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsR8Blit()
{
return g_supports_r8_blit;
} // supportsR8Blit
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsDescriptorIndexing()
{
return g_supports_descriptor_indexing;
} // supportsDescriptorIndexing
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsNonUniformIndexing()
{
return g_supports_non_uniform_indexing;
} // supportsNonUniformIndexing
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsDifferentTexturePerDraw()
{
return g_supports_bind_textures_at_once &&
g_supports_descriptor_indexing && g_supports_non_uniform_indexing;
} // supportsDifferentTexturePerDraw
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsPartiallyBound()
{
return g_supports_partially_bound;
} // supportsPartiallyBound
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsBindMeshTexturesAtOnce()
{
if (!g_supports_bind_textures_at_once || !g_supports_multi_draw_indirect ||
!g_supports_shader_draw_parameters)
return false;
const unsigned sampler_count = GEVulkanShaderManager::getSamplerSize() *
GEVulkanShaderManager::getMeshTextureLayer();
return g_max_sampler_supported >= sampler_count;
} // supportsBindMeshTexturesAtOnce
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsMultiDrawIndirect()
{
return g_supports_multi_draw_indirect;
} // supportsMultiDrawIndirect
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsBaseVertexRendering()
{
return g_supports_base_vertex_rendering;
} // supportsBaseVertexRendering
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsComputeInMainQueue()
{
return g_supports_compute_in_main_queue;
} // supportsComputeInMainQueue
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsShaderDrawParameters()
{
return g_supports_shader_draw_parameters;
} // supportsShaderDrawParameters
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsS3TCBC3()
{
return g_supports_s3tc_bc3;
} // supportsS3TCBC3
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsBPTCBC7()
{
return g_supports_bptc_bc7;
} // supportsBPTCBC7
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsASTC4x4()
{
return g_supports_astc_4x4 && GECompressorASTC4x4::loaded();
} // supportsASTC4x4
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsShaderStorageImageExtendedFormats()
{
return g_supports_shader_storage_image_extended_format;
} // supportsShaderStorageImageExtendedFormats
}
@@ -0,0 +1,391 @@
#include "ge_vulkan_hiz_depth.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_array_texture.hpp"
#include "ge_vulkan_attachment_texture.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
#include "ge_vulkan_deferred_fbo.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include <algorithm>
#include <stdexcept>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanHiZDepth::GEVulkanHiZDepth(GEVulkanDriver* vk)
: m_vk(vk), m_hiz_depth(NULL),
m_descriptor_layout(VK_NULL_HANDLE),
m_pipeline_layout(VK_NULL_HANDLE),
m_pipeline(VK_NULL_HANDLE),
m_descriptor_pool(VK_NULL_HANDLE),
m_rendering_descriptor_pool(VK_NULL_HANDLE),
m_rendering_descriptor_set(VK_NULL_HANDLE)
{
} // GEVulkanHiZDepth
// ----------------------------------------------------------------------------
GEVulkanHiZDepth::~GEVulkanHiZDepth()
{
destroy();
} // ~GEVulkanHiZDepth
// ----------------------------------------------------------------------------
void GEVulkanHiZDepth::prepare(GEVulkanCameraSceneNode* cam)
{
irr::core::recti hiz_size(irr::core::position2di(
cam->getUBOData()->m_viewport.UpperLeftCorner.X,
cam->getUBOData()->m_viewport.UpperLeftCorner.Y),
irr::core::dimension2du(
cam->getUBOData()->m_viewport.LowerRightCorner.X,
cam->getUBOData()->m_viewport.LowerRightCorner.Y));
if (m_hiz_size != hiz_size)
{
m_hiz_size = hiz_size;
destroy();
init();
}
} // prepare
// ----------------------------------------------------------------------------
void GEVulkanHiZDepth::init()
{
m_hiz_depth = new GEVulkanArrayTexture(VK_FORMAT_R32_SFLOAT,
VK_IMAGE_VIEW_TYPE_2D,
irr::core::dimension2du(m_hiz_size.getWidth(), m_hiz_size.getHeight()),
1, irr::video::SColor(0), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
std::array<VkDescriptorSetLayoutBinding, 2> bindings = {};
// Input depth buffer
bindings[0].binding = 0;
bindings[0].descriptorCount = 1;
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
// Output storage image
bindings[1].binding = 1;
bindings[1].descriptorCount = 1;
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorSetLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_info.bindingCount = bindings.size();
layout_info.pBindings = bindings.data();
VkDevice device = m_vk->getDevice();
if (vkCreateDescriptorSetLayout(device, &layout_info, NULL,
&m_descriptor_layout) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create descriptor set layout for "
"HiZ depth");
}
VkPushConstantRange push_constant = {};
push_constant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
push_constant.offset = 0;
push_constant.size = sizeof(uint32_t) * 3;
VkPipelineLayoutCreateInfo pipeline_layout_info = {};
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.setLayoutCount = 1;
pipeline_layout_info.pSetLayouts = &m_descriptor_layout;
pipeline_layout_info.pushConstantRangeCount = 1;
pipeline_layout_info.pPushConstantRanges = &push_constant;
if (vkCreatePipelineLayout(device, &pipeline_layout_info, NULL,
&m_pipeline_layout) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create pipeline layout for HiZ "
"depth");
}
VkComputePipelineCreateInfo pipeline_info = {};
pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
pipeline_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
pipeline_info.stage.module = GEVulkanShaderManager::getShader("hiz_depth.comp");
pipeline_info.stage.pName = "main";
pipeline_info.layout = m_pipeline_layout;
if (vkCreateComputePipelines(device, VK_NULL_HANDLE, 1,
&pipeline_info, NULL, &m_pipeline) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create compute pipeline for HiZ "
"depth");
}
// Create descriptor pool
const uint32_t mip_levels = m_hiz_depth->getMipmapLevels();
std::array<VkDescriptorPoolSize, 2> pool_sizes = {};
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[0].descriptorCount = mip_levels;
pool_sizes[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
pool_sizes[1].descriptorCount = mip_levels;
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.maxSets = mip_levels;
pool_info.poolSizeCount = pool_sizes.size();
pool_info.pPoolSizes = pool_sizes.data();
if (vkCreateDescriptorPool(device, &pool_info, NULL,
&m_descriptor_pool) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create descriptor pool for HiZ depth");
}
// Create descriptor sets for each mip level
m_descriptor_sets.resize(mip_levels);
std::vector<VkDescriptorSetLayout> layouts(mip_levels,
m_descriptor_layout);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = m_descriptor_sets.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(device, &alloc_info,
m_descriptor_sets.data()) != VK_SUCCESS)
{
throw std::runtime_error("Failed to allocate descriptor sets for HiZ "
"depth");
}
// Create image views for each mip level
m_hiz_views.resize(mip_levels);
for (uint32_t i = 0; i < m_hiz_views.size(); i++)
{
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = m_hiz_depth->getImage();
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = m_hiz_depth->getInternalFormat();
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = i;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &view_info, NULL,
&m_hiz_views[i]) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create image view for HiZ "
"depth");
}
}
GEVulkanDeferredFBO* dfbo =
static_cast<GEVulkanDeferredFBO*>(m_vk->getRTTTexture());
for (uint32_t i = 0; i < mip_levels; i++)
{
VkDescriptorImageInfo input_info = {};
input_info.imageLayout = i == 0 ?
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL :
VK_IMAGE_LAYOUT_GENERAL;
input_info.imageView = i == 0 ?
(VkImageView)dfbo->getDepthTexture()->getTextureHandler() :
(VkImageView)m_hiz_depth->getTextureHandler();
input_info.sampler = m_vk->getSampler(GVS_SKYBOX);
VkDescriptorImageInfo output_info = {};
output_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
output_info.imageView = m_hiz_views[i];
std::array<VkWriteDescriptorSet, 2> descriptor_writes = {};
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[0].dstSet = m_descriptor_sets[i];
descriptor_writes[0].dstBinding = 0;
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_writes[0].descriptorCount = 1;
descriptor_writes[0].pImageInfo = &input_info;
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_writes[1].dstSet = m_descriptor_sets[i];
descriptor_writes[1].dstBinding = 1;
descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
descriptor_writes[1].descriptorCount = 1;
descriptor_writes[1].pImageInfo = &output_info;
vkUpdateDescriptorSets(m_vk->getDevice(), descriptor_writes.size(),
descriptor_writes.data(), 0, NULL);
}
loadRenderingDescriptor();
} // init
// ----------------------------------------------------------------------------
void GEVulkanHiZDepth::loadRenderingDescriptor()
{
const size_t displace_binding_count = 3;
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptorCount = displace_binding_count;
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = 1;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(m_vk->getDevice(), &pool_info, NULL,
&m_rendering_descriptor_pool) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"m_rendering_descriptor_pool in GEVulkanHiZDepth");
}
GEVulkanDeferredFBO* dfbo =
static_cast<GEVulkanDeferredFBO*>(m_vk->getRTTTexture());
std::vector<VkDescriptorSetLayout> layouts(1,
dfbo->getDescriptorSetLayout(GVDFP_DISPLACE_COLOR));
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_rendering_descriptor_pool;
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(m_vk->getDevice(), &alloc_info,
&m_rendering_descriptor_set) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"m_rendering_descriptor_set in GEVulkanHiZDepth");
}
std::array<VkDescriptorImageInfo, displace_binding_count> image_infos = {};
image_infos[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[0].imageView =
(VkImageView)
dfbo->getAttachment<GVDFT_DISPLACE_COLOR>()->getTextureHandler();
image_infos[0].sampler = m_vk->getSampler(GVS_NEAREST);
image_infos[1].imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
image_infos[1].imageView =
(VkImageView)dfbo->getDepthTexture()->getTextureHandler();
image_infos[1].sampler = m_vk->getSampler(GVS_SHADOW);
image_infos[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_infos[2].imageView =
(VkImageView)m_hiz_depth->getTextureHandler();
image_infos[2].sampler = m_vk->getSampler(GVS_SKYBOX);
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = image_infos.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_rendering_descriptor_set;
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(m_vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
} // loadRenderingDescriptor
// ----------------------------------------------------------------------------
void GEVulkanHiZDepth::destroy()
{
delete m_hiz_depth;
m_hiz_depth = NULL;
VkDevice device = m_vk->getDevice();
if (m_pipeline != VK_NULL_HANDLE)
vkDestroyPipeline(device, m_pipeline, NULL);
m_pipeline = VK_NULL_HANDLE;
if (m_pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(device, m_pipeline_layout, NULL);
m_pipeline_layout = VK_NULL_HANDLE;
if (m_rendering_descriptor_pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(device, m_rendering_descriptor_pool, NULL);
m_rendering_descriptor_pool = VK_NULL_HANDLE;
m_rendering_descriptor_set = VK_NULL_HANDLE;
if (m_descriptor_pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(device, m_descriptor_pool, NULL);
m_descriptor_pool = VK_NULL_HANDLE;
if (m_descriptor_layout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(device, m_descriptor_layout, NULL);
m_descriptor_layout = VK_NULL_HANDLE;
for (VkImageView view : m_hiz_views)
vkDestroyImageView(device, view, NULL);
m_hiz_views.clear();
m_descriptor_sets.clear();
} // destroy
// ----------------------------------------------------------------------------
void GEVulkanHiZDepth::generate(VkCommandBuffer cmd)
{
const uint32_t mip_levels = m_hiz_depth->getMipmapLevels();
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_hiz_depth->getImage();
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mip_levels;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);
vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
0, sizeof(uint32_t) * 2, &m_hiz_size.UpperLeftCorner.X);
for (uint32_t i = 0; i < mip_levels; i++)
{
// Bind descriptor set and push mip level constant
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE,
m_pipeline_layout, 0, 1, &m_descriptor_sets[i], 0, NULL);
vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT,
sizeof(uint32_t) * 2, sizeof(uint32_t), &i);
// Calculate dispatch size based on current mip level dimensions
const uint32_t width = m_hiz_depth->getSize().Width >> i;
const uint32_t height = m_hiz_depth->getSize().Height >> i;
const uint32_t group_size_x = std::max(1u, (width + 15) / 16);
const uint32_t group_size_y = std::max(1u, (height + 15) / 16);
vkCmdDispatch(cmd, group_size_x, group_size_y, 1);
// Add memory barrier between mip level generations
if (i < mip_levels - 1)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.subresourceRange.baseMipLevel = i;
barrier.subresourceRange.levelCount = 1;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
}
}
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = mip_levels;
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
} // generate
}
@@ -0,0 +1,63 @@
#ifndef HEADER_GE_VULKAN_HIZ_DEPTH_HPP
#define HEADER_GE_VULKAN_HIZ_DEPTH_HPP
#include "vulkan_wrapper.h"
#include "rect.h"
#include <cstdint>
#include <vector>
namespace GE
{
class GEVulkanCameraSceneNode;
class GEVulkanDeferredFBO;
class GEVulkanDriver;
class GEVulkanTexture;
class GEVulkanHiZDepth
{
private:
GEVulkanDriver* m_vk;
GEVulkanTexture* m_hiz_depth;
VkDescriptorSetLayout m_descriptor_layout;
VkPipelineLayout m_pipeline_layout;
VkPipeline m_pipeline;
VkDescriptorPool m_descriptor_pool, m_rendering_descriptor_pool;
VkDescriptorSet m_rendering_descriptor_set;
std::vector<VkDescriptorSet> m_descriptor_sets;
std::vector<VkImageView> m_hiz_views;
irr::core::recti m_hiz_size;
// ------------------------------------------------------------------------
void destroy();
// ------------------------------------------------------------------------
void init();
// ------------------------------------------------------------------------
void loadRenderingDescriptor();
public:
// ------------------------------------------------------------------------
GEVulkanHiZDepth(GEVulkanDriver* vk);
// ------------------------------------------------------------------------
~GEVulkanHiZDepth();
// ------------------------------------------------------------------------
void prepare(GEVulkanCameraSceneNode* cam);
// ------------------------------------------------------------------------
void generate(VkCommandBuffer cmd);
// ------------------------------------------------------------------------
const VkDescriptorSet* getRenderingDescriptorSet() const
{ return &m_rendering_descriptor_set; }
};
}
#endif
@@ -0,0 +1,143 @@
#include "ge_vulkan_light_handler.hpp"
#include "ge_main.hpp"
#include "ge_occlusion_culling.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_fbo_texture.hpp"
#include "ge_vulkan_skybox_renderer.hpp"
#include "ILightSceneNode.h"
#include "ISceneManager.h"
#include "IrrlichtDevice.h"
#include <algorithm>
#include <cmath>
#include <functional>
#include <iterator>
namespace GE
{
using namespace irr;
// ----------------------------------------------------------------------------
void GEVulkanLightHandler::prepare()
{
m_buffer = {};
m_lights.clear();
m_fullscreen_light_count = 0;
video::SColorf c = m_vk->getIrrlichtDevice()->getSceneManager()
->getAmbientLight();
m_buffer.m_ambient_color.X = c.r * c.a;
m_buffer.m_ambient_color.Y = c.g * c.a;
m_buffer.m_ambient_color.Z = c.b * c.a;
m_buffer.m_sun_scatter = 0.2f;
m_buffer.m_sun_color = core::vector3df(0.75f, 0.75f, 0.75f);
m_buffer.m_sun_angle_tan_half = 0.0022f;
m_buffer.m_sun_direction = core::vector3df(0.15f, 0.2f, 1.0f).normalize();
m_buffer.m_skytop_color.X = 0.325f;
m_buffer.m_skytop_color.Y = 0.35f;
m_buffer.m_skytop_color.Z = 0.375f;
} // prepare
// ----------------------------------------------------------------------------
void GEVulkanLightHandler::generate(const irr::core::vector3df& cam_pos,
GEVulkanSkyBoxRenderer* skybox)
{
if (skybox)
{
irr::video::SColorf c(
srgb255ToLinearFromSColor(skybox->getSkytopColor()));
m_buffer.m_skytop_color.X = c.r;
m_buffer.m_skytop_color.Y = c.g;
m_buffer.m_skytop_color.Z = c.b;
}
if (m_lights.size() > MAX_RENDERING_LIGHT)
{
std::sort(m_lights.begin(), m_lights.end(),
[cam_pos](GELight& a, GELight& b)
{
float al = a.m_position.getDistanceFromSQ(cam_pos);
float bl = b.m_position.getDistanceFromSQ(cam_pos);
return al < bl;
});
m_lights.resize(MAX_RENDERING_LIGHT);
}
if (m_lights.empty())
return;
GEVulkanFBOTexture* t =
static_cast<GEVulkanDriver*>(getDriver())->getRTTTexture();
if (t && t->isDeferredFBO())
{
auto i = std::partition(m_lights.begin(), m_lights.end(),
[cam_pos](const GELight& l)
{
float radius_2 = l.m_radius * l.m_radius;
float distance_2 = (cam_pos - l.m_position).getLengthSQ();
return distance_2 <= radius_2;
});
m_fullscreen_light_count = std::distance(m_lights.begin(), i);
}
// Deferred fbo supports light culling using depth test
if (hasOcclusionCulling() && (!t || !t->isDeferredFBO()))
{
auto l = m_lights.begin();
auto rl = m_buffer.m_rendering_lights.begin();
while (l != m_lights.end())
{
if (getOcclusionCulling()->isOccluded(cam_pos, l->m_position,
l->m_radius))
{
l++;
continue;
}
*rl = *l;
l++;
rl++;
}
m_buffer.m_light_count = rl - m_buffer.m_rendering_lights.begin();
}
else
{
std::copy(m_lights.begin(), m_lights.end(),
m_buffer.m_rendering_lights.begin());
m_buffer.m_light_count = m_lights.size();
}
} // generate
// ----------------------------------------------------------------------------
void GEVulkanLightHandler::addLightNode(irr::scene::ILightSceneNode* node)
{
const video::SLight& l = node->getLightData();
if (node->getLightType() == irr::video::ELT_DIRECTIONAL)
{
m_buffer.m_sun_color.X = l.DiffuseColor.r;
m_buffer.m_sun_color.Y = l.DiffuseColor.g;
m_buffer.m_sun_color.Z = l.DiffuseColor.b;
m_buffer.m_sun_scatter = l.DiffuseColor.a;
core::vector3df dir = l.Direction;
m_buffer.m_sun_direction = -dir.normalize();
m_buffer.m_sun_angle_tan_half = tanf(l.Radius * 0.5f);
}
else
{
GELight gl = {};
gl.m_position = l.Position;
gl.m_radius = l.Radius;
gl.m_color.X = l.DiffuseColor.r * l.Attenuation.X;
gl.m_color.Y = l.DiffuseColor.g * l.Attenuation.X;
gl.m_color.Z = l.DiffuseColor.b * l.Attenuation.X;
gl.m_inverse_range_squared = l.Attenuation.Y * l.Attenuation.Y;
if (l.Type == irr::video::ELT_SPOT)
{
gl.m_direction.X = l.Direction.X;
gl.m_direction.Y = l.Direction.Y;
float cos_outer = cosf(l.OuterCone);
gl.m_scale = 1.0f / std::max(cosf(l.InnerCone) - cos_outer, 1e-4f);
gl.m_offset = -cos_outer * gl.m_scale;
gl.m_scale *= l.Direction.Z > 0.f ? 1.f : -1.f;
}
m_lights.push_back(gl);
}
} // addLightNode
}
@@ -0,0 +1,92 @@
#ifndef HEADER_GE_VULKAN_LIGHT_HANDLER_HPP
#define HEADER_GE_VULKAN_LIGHT_HANDLER_HPP
#include "vector2d.h"
#include "vector3d.h"
#include "SColor.h"
#include <array>
#include <vector>
namespace irr
{
namespace scene
{
class ILightSceneNode;
}
}
namespace GE
{
class GEVulkanDriver;
class GEVulkanSkyBoxRenderer;
const irr::u32 MAX_RENDERING_LIGHT = 32;
struct GELight
{
irr::core::vector3df m_position;
irr::f32 m_radius;
irr::core::vector3df m_color;
irr::f32 m_inverse_range_squared;
irr::core::vector2df m_direction;
irr::f32 m_scale;
irr::f32 m_offset;
};
struct GEGlobalLightBuffer
{
irr::core::vector3df m_ambient_color;
irr::f32 m_sun_scatter;
irr::core::vector3df m_sun_color;
irr::f32 m_sun_angle_tan_half;
irr::core::vector3df m_sun_direction;
irr::f32 m_fog_density;
irr::video::SColorf m_fog_color;
irr::core::vector3df m_skytop_color;
irr::u32 m_light_count;
std::array<GELight, MAX_RENDERING_LIGHT> m_rendering_lights;
};
class GEVulkanLightHandler
{
private:
GEVulkanDriver* m_vk;
GEGlobalLightBuffer m_buffer;
std::vector<GELight> m_lights;
unsigned m_fullscreen_light_count;
public:
// ------------------------------------------------------------------------
GEVulkanLightHandler(GEVulkanDriver* vk)
{
m_vk = vk;
prepare();
}
// ------------------------------------------------------------------------
~GEVulkanLightHandler() {}
// ------------------------------------------------------------------------
void prepare();
// ------------------------------------------------------------------------
void generate(const irr::core::vector3df& cam_pos,
GEVulkanSkyBoxRenderer* skybox);
// ------------------------------------------------------------------------
void addLightNode(irr::scene::ILightSceneNode* node);
// ------------------------------------------------------------------------
void* getData() { return &m_buffer; }
// ------------------------------------------------------------------------
size_t getSize() const
{
return sizeof(GEGlobalLightBuffer) -
(sizeof(GELight) * (MAX_RENDERING_LIGHT - m_buffer.m_light_count));
}
// ------------------------------------------------------------------------
unsigned getLightCount() const { return m_buffer.m_light_count; }
// ------------------------------------------------------------------------
unsigned getFullscreenLightCount() const
{ return m_fullscreen_light_count; }
}; // GEVulkanLightHandler
}
#endif
@@ -0,0 +1,191 @@
#include "ge_vulkan_mesh_cache.hpp"
#include "ge_main.hpp"
#include "ge_spm_buffer.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include "IAnimatedMesh.h"
#include <algorithm>
#include <cassert>
#include <stdexcept>
#include <vector>
#include <stdexcept>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanMeshCache::GEVulkanMeshCache()
: irr::scene::CMeshCache()
{
m_vk = getVKDriver();
m_irrlicht_cache_time = getMonoTimeMs();
m_ge_cache_time = 0;
m_buffer = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
m_ibo_offset = m_skinning_vbo_offset = 0;
} // init
// ----------------------------------------------------------------------------
void GEVulkanMeshCache::meshCacheChanged()
{
m_irrlicht_cache_time = getMonoTimeMs();
} // meshCacheChanged
// ----------------------------------------------------------------------------
void GEVulkanMeshCache::updateCache()
{
if (!GEVulkanFeatures::supportsBaseVertexRendering())
return;
if (m_irrlicht_cache_time <= m_ge_cache_time)
return;
m_ge_cache_time = m_irrlicht_cache_time;
destroy();
size_t total_pitch = getVertexPitchFromType(video::EVT_SKINNED_MESH);
size_t bone_pitch = sizeof(int16_t) * 8;
size_t static_pitch = total_pitch - bone_pitch;
size_t vbo_size, ibo_size;
vbo_size = 0;
ibo_size = 0;
std::vector<GESPMBuffer*> buffers;
for (unsigned i = 0; i < Meshes.size(); i++)
{
scene::IAnimatedMesh* mesh = Meshes[i].Mesh;
if (mesh->getMeshType() != scene::EAMT_SPM)
continue;
for (unsigned j = 0; j < mesh->getMeshBufferCount(); j++)
{
GESPMBuffer* mb = static_cast<GESPMBuffer*>(mesh->getMeshBuffer(j));
size_t pitch = mb->hasSkinning() ? total_pitch : static_pitch;
vbo_size += mb->getVertexCount() * pitch;
ibo_size += mb->getIndexCount();
buffers.push_back(mb);
}
}
ibo_size *= sizeof(uint16_t);
// Some devices (Apple for now) require vertex offset of alignment 4 bytes
ibo_size += getPadding(ibo_size, 4);
std::stable_partition(buffers.begin(), buffers.end(),
[](const GESPMBuffer* mb)
{
return !mb->hasSkinning();
});
VkBuffer staging_buffer = VK_NULL_HANDLE;
VmaAllocation staging_memory = VK_NULL_HANDLE;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
staging_buffer_create_info.flags =
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (!m_vk->createBuffer(vbo_size + ibo_size,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_memory))
throw std::runtime_error("updateCache create staging buffer failed");
uint8_t* mapped;
if (vmaMapMemory(m_vk->getVmaAllocator(), staging_memory,
(void**)&mapped) != VK_SUCCESS)
throw std::runtime_error("updateCache vmaMapMemory failed");
size_t offset = 0;
for (GESPMBuffer* spm_buffer : buffers)
{
copyToMappedBuffer((uint32_t*)(mapped + offset), spm_buffer);
spm_buffer->setVBOOffset(offset / static_pitch);
size_t copy_size = spm_buffer->getVertexCount() * static_pitch;
offset += copy_size;
}
m_ibo_offset = offset;
offset = 0;
for (GESPMBuffer* spm_buffer : buffers)
{
size_t copy_size = spm_buffer->getIndexCount() * sizeof(uint16_t);
uint8_t* loc = mapped + offset + m_ibo_offset;
memcpy(loc, spm_buffer->getIndices(), copy_size);
spm_buffer->setIBOOffset(offset / sizeof(uint16_t));
offset += copy_size;
}
m_skinning_vbo_offset = m_ibo_offset + offset;
m_skinning_vbo_offset += getPadding(m_skinning_vbo_offset, 4);
offset = 0;
size_t static_vertex_offset = 0;
for (GESPMBuffer* spm_buffer : buffers)
{
if (!spm_buffer->hasSkinning())
{
static_vertex_offset += spm_buffer->getVertexCount() * bone_pitch;
continue;
}
uint8_t* loc = mapped + offset + m_skinning_vbo_offset;
size_t real_size = spm_buffer->getVertexCount() * total_pitch;
for (unsigned i = 0; i < real_size; i += total_pitch)
{
uint8_t* vertices = ((uint8_t*)spm_buffer->getVertices()) + i +
static_pitch;
memcpy(loc, vertices, bone_pitch);
loc += bone_pitch;
}
offset += spm_buffer->getVertexCount() * bone_pitch;
}
assert(m_skinning_vbo_offset + offset == vbo_size + ibo_size);
assert(static_vertex_offset < m_skinning_vbo_offset);
m_skinning_vbo_offset -= static_vertex_offset;
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_memory);
vmaFlushAllocation(m_vk->getVmaAllocator(), staging_memory, 0, offset);
VmaAllocationCreateInfo local_create_info = {};
local_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
local_create_info.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
if (!m_vk->createBuffer(vbo_size + ibo_size,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT, local_create_info, m_buffer,
m_memory))
throw std::runtime_error("updateCache create buffer failed");
m_vk->copyBuffer(staging_buffer, m_buffer, vbo_size + ibo_size);
vmaDestroyBuffer(m_vk->getVmaAllocator(), staging_buffer, staging_memory);
} // updateCache
// ----------------------------------------------------------------------------
void GEVulkanMeshCache::destroy()
{
m_vk->waitIdle();
m_vk->setDisableWaitIdle(true);
if (!GEVulkanFeatures::supportsBaseVertexRendering())
{
for (unsigned i = 0; i < Meshes.size(); i++)
{
scene::IAnimatedMesh* mesh = Meshes[i].Mesh;
if (mesh->getMeshType() != scene::EAMT_SPM)
continue;
for (unsigned j = 0; j < mesh->getMeshBufferCount(); j++)
{
GESPMBuffer* mb = static_cast<GESPMBuffer*>(
mesh->getMeshBuffer(j));
mb->destroyVertexIndexBuffer();
}
}
}
else
{
vmaDestroyBuffer(m_vk->getVmaAllocator(), m_buffer, m_memory);
m_buffer = VK_NULL_HANDLE;
m_memory = VK_NULL_HANDLE;
}
m_vk->setDisableWaitIdle(false);
m_ibo_offset = m_skinning_vbo_offset = 0;
} // destroy
}
@@ -0,0 +1,44 @@
#ifndef HEADER_GE_VULKAN_MESH_CACHE_HPP
#define HEADER_GE_VULKAN_MESH_CACHE_HPP
#include "vulkan_wrapper.h"
#include "ge_vma.hpp"
#include "../source/Irrlicht/CMeshCache.h"
#include <cstdint>
namespace GE
{
class GEVulkanDriver;
class GEVulkanMeshCache : public irr::scene::CMeshCache
{
private:
GEVulkanDriver* m_vk;
uint64_t m_irrlicht_cache_time, m_ge_cache_time;
VkBuffer m_buffer;
VmaAllocation m_memory;
size_t m_ibo_offset, m_skinning_vbo_offset;
public:
// ------------------------------------------------------------------------
GEVulkanMeshCache();
// ------------------------------------------------------------------------
virtual void meshCacheChanged();
// ------------------------------------------------------------------------
void updateCache();
// ------------------------------------------------------------------------
void destroy();
// ------------------------------------------------------------------------
VkBuffer getBuffer() const { return m_buffer; }
// ------------------------------------------------------------------------
size_t getIBOOffset() const { return m_ibo_offset; }
// ------------------------------------------------------------------------
size_t getSkinningVBOOffset() const { return m_skinning_vbo_offset; }
}; // GEVulkanMeshCache
}
#endif
@@ -0,0 +1,43 @@
#include "ge_vulkan_mesh_scene_node.hpp"
#include "ge_spm.hpp"
#include "IMeshCache.h"
#include "ISceneManager.h"
namespace GE
{
GEVulkanMeshSceneNode::GEVulkanMeshSceneNode(irr::scene::IMesh* mesh,
irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
const irr::core::vector3df& position,
const irr::core::vector3df& rotation,
const irr::core::vector3df& scale)
: irr::scene::CMeshSceneNode(mesh, parent, mgr, id, position, rotation,
scale)
{
m_remove_from_mesh_cache = false;
} // GEVulkanMeshSceneNode
// ----------------------------------------------------------------------------
GEVulkanMeshSceneNode::~GEVulkanMeshSceneNode()
{
if (m_remove_from_mesh_cache)
SceneManager->getMeshCache()->removeMesh(Mesh);
} // ~GEVulkanMeshSceneNode
// ----------------------------------------------------------------------------
GESPM* GEVulkanMeshSceneNode::getSPM() const
{
return static_cast<GESPM*>(Mesh);
} // getSPM
// ----------------------------------------------------------------------------
void GEVulkanMeshSceneNode::OnRegisterSceneNode()
{
if (!IsVisible)
return;
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
ISceneNode::OnRegisterSceneNode();
} // OnRegisterSceneNode
}
@@ -0,0 +1,33 @@
#ifndef HEADER_GE_VULKAN_MESH_SCENE_NODE_HPP
#define HEADER_GE_VULKAN_MESH_SCENE_NODE_HPP
#include "../source/Irrlicht/CMeshSceneNode.h"
namespace GE
{
class GESPM;
class GEVulkanMeshSceneNode : public irr::scene::CMeshSceneNode
{
private:
bool m_remove_from_mesh_cache;
public:
// ------------------------------------------------------------------------
GEVulkanMeshSceneNode(irr::scene::IMesh* mesh,
irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0),
const irr::core::vector3df& rotation = irr::core::vector3df(0, 0, 0),
const irr::core::vector3df& scale = irr::core::vector3df(1.0f, 1.0f, 1.0f));
// ------------------------------------------------------------------------
~GEVulkanMeshSceneNode();
// ------------------------------------------------------------------------
void setRemoveFromMeshCache(bool val) { m_remove_from_mesh_cache = val; }
// ------------------------------------------------------------------------
GESPM* getSPM() const;
// ------------------------------------------------------------------------
virtual void OnRegisterSceneNode();
}; // GEVulkanMeshSceneNode
}
#endif
@@ -0,0 +1,352 @@
#include "ge_vulkan_scene_manager.hpp"
#include "../source/Irrlicht/os.h"
#include "ge_main.hpp"
#include "ge_material_manager.hpp"
#include "ge_vulkan_animated_mesh_scene_node.hpp"
#include "ge_vulkan_camera_scene_node.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_deferred_fbo.hpp"
#include "ge_vulkan_draw_call.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_light_handler.hpp"
#include "ge_vulkan_mesh_cache.hpp"
#include "ge_vulkan_mesh_scene_node.hpp"
#include "ge_vulkan_skybox_renderer.hpp"
#include "ge_vulkan_texture_descriptor.hpp"
#include "IBillboardSceneNode.h"
#include "ILightSceneNode.h"
#include <sstream>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanSceneManager::GEVulkanSceneManager(irr::video::IVideoDriver* driver,
irr::io::IFileSystem* fs,
irr::gui::ICursorControl* cursor_control,
irr::gui::IGUIEnvironment* gui_environment)
: CSceneManager(driver, fs, cursor_control,
new GEVulkanMeshCache(), gui_environment)
{
resetDetectDeferred();
// CSceneManager grabbed it
getMeshCache()->drop();
} // GEVulkanSceneManager
// ----------------------------------------------------------------------------
GEVulkanSceneManager::~GEVulkanSceneManager()
{
} // ~GEVulkanSceneManager
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::clear()
{
irr::scene::CSceneManager::clear();
static_cast<GEVulkanDriver*>(getVideoDriver())
->getMeshTextureDescriptor()->clear();
} // clear
// ----------------------------------------------------------------------------
irr::scene::ICameraSceneNode* GEVulkanSceneManager::addCameraSceneNode(
irr::scene::ISceneNode* parent,
const irr::core::vector3df& position,
const irr::core::vector3df& lookat,
irr::s32 id, bool make_active)
{
if (!parent)
parent = this;
irr::scene::ICameraSceneNode* node = new GEVulkanCameraSceneNode(parent,
this, id, position, lookat);
if (make_active)
setActiveCamera(node);
node->drop();
return node;
} // addCameraSceneNode
// ----------------------------------------------------------------------------
irr::scene::IAnimatedMeshSceneNode* GEVulkanSceneManager::addAnimatedMeshSceneNode(
irr::scene::IAnimatedMesh* mesh, irr::scene::ISceneNode* parent,
irr::s32 id,
const irr::core::vector3df& position,
const irr::core::vector3df& rotation,
const irr::core::vector3df& scale,
bool alsoAddIfMeshPointerZero)
{
if (!alsoAddIfMeshPointerZero && (!mesh ||
mesh->getMeshType() != irr::scene::EAMT_SPM))
return NULL;
if (!parent)
parent = this;
irr::scene::IAnimatedMeshSceneNode* node =
new GEVulkanAnimatedMeshSceneNode(mesh, parent, this, id, position,
rotation, scale);
node->drop();
node->setMesh(mesh);
return node;
} // addAnimatedMeshSceneNode
// ----------------------------------------------------------------------------
irr::scene::IMeshSceneNode* GEVulkanSceneManager::addMeshSceneNode(
irr::scene::IMesh* mesh,
irr::scene::ISceneNode* parent, irr::s32 id,
const irr::core::vector3df& position,
const irr::core::vector3df& rotation,
const irr::core::vector3df& scale,
bool alsoAddIfMeshPointerZero)
{
if (!alsoAddIfMeshPointerZero && !mesh)
return NULL;
bool convert_irrlicht_mesh = false;
if (mesh)
{
for (unsigned i = 0; i < mesh->getMeshBufferCount(); i++)
{
irr::scene::IMeshBuffer* b = mesh->getMeshBuffer(i);
if (b->getVertexType() != irr::video::EVT_SKINNED_MESH)
{
if (!getGEConfig()->m_convert_irrlicht_mesh)
{
return irr::scene::CSceneManager::addMeshSceneNode(
mesh, parent, id, position, rotation, scale,
alsoAddIfMeshPointerZero);
}
else
{
convert_irrlicht_mesh = true;
break;
}
}
}
}
if (!parent)
parent = this;
if (convert_irrlicht_mesh)
{
irr::scene::IAnimatedMesh* spm = convertIrrlichtMeshToSPM(mesh);
std::stringstream oss;
oss << (uint64_t)spm;
getMeshCache()->addMesh(oss.str().c_str(), spm);
mesh = spm;
}
GEVulkanMeshSceneNode* vulkan_node =
new GEVulkanMeshSceneNode(mesh, parent, this, id, position, rotation,
scale);
irr::scene::IMeshSceneNode* node = vulkan_node;
node->drop();
if (convert_irrlicht_mesh)
{
vulkan_node->setRemoveFromMeshCache(true);
mesh->drop();
}
return node;
} // addMeshSceneNode
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::drawAllInternal()
{
static_cast<GEVulkanMeshCache*>(getMeshCache())->updateCache();
GEVulkanCameraSceneNode* cam = NULL;
if (getActiveCamera())
{
cam = static_cast<
GEVulkanCameraSceneNode*>(getActiveCamera());
}
OnAnimate(os::Timer::getTime());
if (cam)
{
cam->render();
auto it = m_draw_calls.find(cam);
if (it == m_draw_calls.end())
return;
it->second->prepare(cam);
OnRegisterSceneNode();
it->second->generate(static_cast<GEVulkanDriver*>(getVideoDriver()));
}
} // drawAllInternal
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::drawAll(irr::u32 flags)
{
drawAllInternal();
GEVulkanDriver* vk = static_cast<GEVulkanDriver*>(getVideoDriver());
GEVulkanFBOTexture* rtt = vk->getSeparateRTTTexture();
if (!rtt)
return;
std::vector<VkClearValue> clear_values(2);
video::SColorf cf(vk->getRTTClearColor());
clear_values[0].color =
{
cf.getRed(), cf.getGreen(), cf.getBlue(), cf.getAlpha()
};
clear_values[1].depthStencil = {1.0f, 0};
unsigned count = rtt->getZeroClearCountForPass(GVDFP_HDR);
VkClearValue zero;
zero.color = {0, 0, 0, 0};
for (unsigned c = 0; c < count; c++)
clear_values.push_back(zero);
VkCommandBuffer cmd = GEVulkanCommandLoader::beginSingleTimeCommands();
GEVulkanCameraSceneNode* cam = static_cast<
GEVulkanCameraSceneNode*>(getActiveCamera());
std::unique_ptr<GEVulkanDrawCall>& dc = m_draw_calls.at(cam);
cam->setViewPort(
core::recti(0, 0, rtt->getSize().Width, rtt->getSize().Height));
cam->render();
dc->uploadDynamicData(vk, cam, cmd);
VkRenderPassBeginInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = rtt->getRTTRenderPass();
render_pass_info.framebuffer = rtt->getRTTFramebuffer();
render_pass_info.renderArea.offset = {0, 0};
render_pass_info.renderArea.extent =
{ rtt->getSize().Width, rtt->getSize().Height };
render_pass_info.clearValueCount = (uint32_t)(clear_values.size());
render_pass_info.pClearValues = &clear_values[0];
vkCmdBeginRenderPass(cmd, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);
vk->renderDrawCalls({{ dc.get(), cam }}, cmd);
vk->addRTTPolyCount(dc->getPolyCount());
dc->reset();
vkCmdEndRenderPass(cmd);
GEVulkanCommandLoader::endSingleTimeCommands(cmd);
vk->handleDeletedTextures();
} // drawAll
// ----------------------------------------------------------------------------
irr::u32 GEVulkanSceneManager::registerNodeForRendering(
irr::scene::ISceneNode* node,
irr::scene::E_SCENE_NODE_RENDER_PASS pass)
{
if (!getActiveCamera())
return 0;
GEVulkanCameraSceneNode* cam = static_cast<
GEVulkanCameraSceneNode*>(getActiveCamera());
if (node->getType() == irr::scene::ESNT_SKY_BOX)
{
m_draw_calls.at(cam)->addSkyBox(node);
return 1;
}
if (node->getType() == irr::scene::ESNT_LIGHT)
{
m_draw_calls.at(cam)->addLightNode(
static_cast<irr::scene::ILightSceneNode*>(node));
return 1;
}
if (node->getType() == irr::scene::ESNT_BILLBOARD ||
node->getType() == irr::scene::ESNT_PARTICLE_SYSTEM)
{
m_draw_calls.at(cam)->addBillboardNode(node, node->getType());
return 1;
}
if ((node->getType() == irr::scene::ESNT_ANIMATED_MESH &&
pass != irr::scene::ESNRP_SOLID) ||
(node->getType() == irr::scene::ESNT_MESH &&
pass != irr::scene::ESNRP_SOLID))
return 0;
m_draw_calls.at(cam)->addNode(node);
return 1;
} // registerNodeForRendering
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::detectDeferred(irr::scene::ISceneNode* node)
{
if (node->isVisible())
{
switch (node->getType())
{
case irr::scene::ESNT_LIGHT:
{
auto l = static_cast<irr::scene::ILightSceneNode*>(node);
if (l->getLightType() == irr::video::ELT_POINT)
m_pointlight_count++;
else if (l->getLightType() == irr::video::ELT_SPOT)
m_spotlight_count++;
break;
}
case irr::scene::ESNT_ANIMATED_MESH:
case irr::scene::ESNT_MESH:
{
for (unsigned i = 0; i < node->getMaterialCount(); i++)
{
irr::video::SMaterial& m = node->getMaterial(i);
if (GEMaterialManager::getShader(m.MaterialType) == "displace")
m_displace_count++;
}
break;
}
default:
break;
}
}
for (unsigned i = 0; i < node->getChildren().size(); i++)
detectDeferred(node->getChildren()[i]);
} // detectDeferred
// ----------------------------------------------------------------------------
GEAutoDeferredType GEVulkanSceneManager::getDetectDeferredResult() const
{
if (m_displace_count > 0)
return GADT_DISPLACE;
#if defined(TILED_GPU)
if (m_spotlight_count > 0 ||
m_pointlight_count > MAX_RENDERING_LIGHT / 2)
return GADT_SINGLE_PASS;
#endif
return GADT_DISABLED;
} // getDetectDeferredResult
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::addDrawCall(GEVulkanCameraSceneNode* cam)
{
GEVulkanDriver* gevk = static_cast<GEVulkanDriver*>(getVideoDriver());
if (!gevk->getSeparateRTTTexture())
{
bool prev_deferred = needsDeferredRendering();
GEAutoDeferredType prev_deferred_type =
getGEConfig()->m_auto_deferred_type;
resetDetectDeferred();
detectDeferred(this);
getGEConfig()->m_auto_deferred_type = getDetectDeferredResult();
if (needsDeferredRendering() != prev_deferred ||
prev_deferred_type != getGEConfig()->m_auto_deferred_type)
gevk->updateDriver();
}
m_draw_calls[cam] = gevk->getDrawCallFromCache();
} // addDrawCall
// ----------------------------------------------------------------------------
void GEVulkanSceneManager::removeDrawCall(GEVulkanCameraSceneNode* cam)
{
if (m_draw_calls.find(cam) == m_draw_calls.end())
return;
GEVulkanDriver* gevk = static_cast<GEVulkanDriver*>(getVideoDriver());
auto& dc = m_draw_calls.at(cam);
gevk->addDrawCallToCache(dc);
m_draw_calls.erase(cam);
} // removeDrawCall
}
@@ -0,0 +1,308 @@
#include "ge_vulkan_shader_manager.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_main.hpp"
#include "ge_spin_lock.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <stdexcept>
#include <utility>
#include "IFileSystem.h"
namespace GE
{
namespace GEVulkanShaderManager
{
// ============================================================================
GEVulkanDriver* g_vk = NULL;
irr::io::IFileSystem* g_file_system = NULL;
std::string g_predefines = "";
uint32_t g_mesh_texture_layer = 2;
uint32_t g_sampler_size = 512;
struct ShaderHolder
{
GESpinLock m_lock;
VkShaderModule m_shader_module;
ShaderHolder() : m_shader_module(VK_NULL_HANDLE) {}
~ShaderHolder()
{
m_lock.lock();
m_lock.unlock();
if (g_vk && m_shader_module != VK_NULL_HANDLE)
vkDestroyShaderModule(g_vk->getDevice(), m_shader_module, NULL);
}
};
std::map<std::string, std::unique_ptr<ShaderHolder> > g_shaders;
} // GEVulkanShaderManager
// ============================================================================
#ifndef DISABLE_SHADERC
shaderc_include_result* showError(const char* message)
{
shaderc_include_result* err = new shaderc_include_result;
err->source_name = "";
err->source_name_length = 0;
err->content = message;
err->content_length = strlen(message);
err->user_data = NULL;
return err;
} // showError
#endif
// ============================================================================
void GEVulkanShaderManager::init(GEVulkanDriver* vk)
{
g_vk = vk;
g_file_system = vk->getFileSystem();
loadAllShaders();
} // init
// ----------------------------------------------------------------------------
void GEVulkanShaderManager::destroy()
{
g_shaders.clear();
g_vk = NULL;
g_file_system = NULL;
} // destroy
// ----------------------------------------------------------------------------
void GEVulkanShaderManager::loadAllShaders(const std::string& match_filename)
{
#ifndef DISABLE_SHADERC
std::ostringstream oss;
oss << "#version 450\n";
if (getGEConfig()->m_pbr)
{
oss << "#define PBR_ENABLED 1\n";
g_mesh_texture_layer = 8;
}
else
g_mesh_texture_layer = 2;
oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n";
oss << "#define TOTAL_MESH_TEXTURE_LAYER " << g_mesh_texture_layer << "\n";
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
oss << "#define BIND_TEXTURES_AT_ONCE\n";
if (GEVulkanFeatures::supportsBindMeshTexturesAtOnce())
oss << "#define BIND_MESH_TEXTURES_AT_ONCE\n";
if (GEVulkanFeatures::supportsDifferentTexturePerDraw())
{
oss << "#extension GL_EXT_nonuniform_qualifier : enable\n";
oss << "#define GE_SAMPLE_TEX_INDEX nonuniformEXT\n";
}
else
oss << "#define GE_SAMPLE_TEX_INDEX int\n";
if (GEVulkanFeatures::supportsShaderStorageImageExtendedFormats())
oss << "#define SHADER_STORAGE_IMAGE_EXTENDED_FORMATS\n";
#if defined(TILED_GPU)
oss << "#define TILED_GPU\n";
#endif
g_predefines = oss.str();
irr::io::IFileList* files = g_file_system->createFileList(
getShaderFolder().c_str());
for (unsigned i = 0; i < files->getFileCount(); i++)
{
if (files->isDirectory(i))
continue;
std::string filename = files->getFileName(i).c_str();
if (!match_filename.empty() &&
filename.find(match_filename) == std::string::npos)
continue;
std::string ext = filename.substr(filename.find_last_of(".") + 1);
shaderc_shader_kind kind;
if (ext == "vert")
kind = shaderc_vertex_shader;
else if (ext == "frag")
kind = shaderc_fragment_shader;
else if (ext == "comp")
kind = shaderc_compute_shader;
else if (ext == "tesc")
kind = shaderc_tess_control_shader;
else if (ext == "tese")
kind = shaderc_tess_evaluation_shader;
else
continue;
g_shaders[filename] = std::unique_ptr<ShaderHolder>(new ShaderHolder);
auto holder = g_shaders.at(filename).get();
holder->m_lock.lock();
GEVulkanCommandLoader::addMultiThreadingCommand(
[holder, kind, filename]()
{
try
{
holder->m_shader_module = loadShader(kind, filename);
}
catch (std::exception& e)
{
printf("%s", e.what());
}
holder->m_lock.unlock();
});
}
files->drop();
#endif
} // loadAllShaders
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::loadShader(shaderc_shader_kind kind,
const std::string& name)
{
#ifdef DISABLE_SHADERC
return VK_NULL_HANDLE;
#else
std::string shader_fullpath = getShaderFolder() + name;
irr::io::IReadFile* r = irr::io::createReadFile(shader_fullpath.c_str());
if (!r)
{
throw std::runtime_error(std::string("File ") + shader_fullpath +
" is missing");
}
std::string shader_data;
shader_data.resize(r->getSize());
int nb_read = 0;
if ((nb_read = r->read(&shader_data[0], r->getSize())) != r->getSize())
{
r->drop();
throw std::runtime_error(
std::string("File ") + name + " failed to be read");
}
r->drop();
shader_data = g_predefines + shader_data;
shaderc_compiler_t compiler = shaderc_compiler_initialize();
shaderc_compile_options_t options = shaderc_compile_options_initialize();
struct FileIncluder
{
std::vector<std::string> m_shader_fullpath;
std::vector<std::string> m_shader_data;
};
FileIncluder includer;
shaderc_compile_options_set_include_callbacks(options,
[](void* user_data, const char* requested_source, int type,
const char* requesting_source, size_t include_depth)
->shaderc_include_result*
{
if (type != shaderc_include_type_relative)
return showError("Only relateive included supported");
std::string path = requesting_source;
size_t pos = path.find_last_of('/');
if (pos == std::string::npos)
pos = path.find_last_of('\\');
if (pos == std::string::npos)
throw std::runtime_error(std::string("Invalid path: ") + path);
FileIncluder* includer = (FileIncluder*)user_data;
includer->m_shader_fullpath.push_back(std::string());
std::string& shader_fullpath = includer->m_shader_fullpath.back();
shader_fullpath = path.substr(0, pos) + '/' + requested_source;
irr::io::IReadFile* r =
irr::io::createReadFile(shader_fullpath.c_str());
if (!r)
{
throw std::runtime_error(std::string("File ") + shader_fullpath
+ " is missing");
}
includer->m_shader_data.push_back(std::string());
std::string& shader_data = includer->m_shader_data.back();
shader_data.resize(r->getSize());
int nb_read = 0;
if ((nb_read = r->read(&shader_data[0], r->getSize())) !=
r->getSize())
{
r->drop();
throw std::runtime_error(std::string("File ") +
requested_source + " failed to be read");
}
r->drop();
shaderc_include_result* result = new shaderc_include_result;
result->source_name = shader_fullpath.c_str();
result->source_name_length = shader_fullpath.size();
result->content = shader_data.c_str();
result->content_length = shader_data.size();
result->user_data = NULL;
return result;
},
[](void* user_data, shaderc_include_result* include_result)
{
delete include_result;
}, &includer);
shaderc_compilation_result_t result = shaderc_compile_into_spv(compiler,
shader_data.c_str(), shader_data.size(), kind, shader_fullpath.c_str(),
"main", options);
shaderc_compile_options_release(options);
shaderc_compilation_status status =
shaderc_result_get_compilation_status(result);
if (status != shaderc_compilation_status_success)
throw std::runtime_error(shaderc_result_get_error_message(result));
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = NULL;
uint32_t* byte_code = (uint32_t*)shaderc_result_get_bytes(result);
size_t byte_code_size = shaderc_result_get_length(result);
create_info.codeSize = byte_code_size;
create_info.pCode = byte_code;
VkShaderModule shader_module;
if (vkCreateShaderModule(g_vk->getDevice(), &create_info, NULL,
&shader_module) != VK_SUCCESS)
{
throw std::runtime_error(
std::string("vkCreateShaderModule failed for ") + name);
}
shaderc_result_release(result);
shaderc_compiler_release(compiler);
return shader_module;
#endif
} // loadShader
// ----------------------------------------------------------------------------
unsigned GEVulkanShaderManager::getSamplerSize()
{
return g_sampler_size;
} // getSamplerSize
// ----------------------------------------------------------------------------
unsigned GEVulkanShaderManager::getMeshTextureLayer()
{
return g_mesh_texture_layer;
} // getMeshTextureLayer
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::getShader(const std::string& filename)
{
if (g_shaders.empty())
{
throw std::runtime_error("No vulkan shaders compiled, perhaps shaderc "
"is not enabled.");
}
auto& it = g_shaders.at(filename);
it->m_lock.lock();
it->m_lock.unlock();
if (it->m_shader_module == VK_NULL_HANDLE)
throw std::runtime_error(std::string("Missing shader ") + filename);
return it->m_shader_module;
} // getShader
}
@@ -0,0 +1,35 @@
#ifndef HEADER_GE_VULKAN_SHADER_MANAGER_HPP
#define HEADER_GE_VULKAN_SHADER_MANAGER_HPP
#include "vulkan_wrapper.h"
#include <string>
#ifdef DISABLE_SHADERC
#define shaderc_shader_kind int
#else
#include <shaderc/shaderc.h>
#endif
namespace GE
{
class GEVulkanDriver;
namespace GEVulkanShaderManager
{
// ----------------------------------------------------------------------------
void init(GEVulkanDriver*);
// ----------------------------------------------------------------------------
void destroy();
// ----------------------------------------------------------------------------
void loadAllShaders(const std::string& match_filename = "");
// ----------------------------------------------------------------------------
VkShaderModule getShader(const std::string& filename);
// ----------------------------------------------------------------------------
VkShaderModule loadShader(shaderc_shader_kind, const std::string&);
// ----------------------------------------------------------------------------
unsigned getSamplerSize();
// ----------------------------------------------------------------------------
unsigned getMeshTextureLayer();
}; // GEVulkanShaderManager
}
#endif
@@ -0,0 +1,316 @@
#include "ge_vulkan_skybox_renderer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_array_texture.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_environment_map.hpp"
#include "ge_vulkan_features.hpp"
#include <array>
#include <cstdint>
#include <stdexcept>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanSkyBoxRenderer::GEVulkanSkyBoxRenderer()
: m_skybox(NULL), m_texture_cubemap(NULL),
m_diffuse_env_cubemap(NULL),
m_specular_env_cubemap(NULL),
m_dummy_env_cubemap(NULL),
m_env_descriptor_layout(VK_NULL_HANDLE),
m_descriptor_pool(VK_NULL_HANDLE),
m_skybox_loading(false), m_env_cubemap_loading(false),
m_skytop_color(0)
{
m_dummy_env_cubemap = new GEVulkanArrayTexture(VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_VIEW_TYPE_CUBE, core::dimension2du(4, 4), 6,
video::SColor(0));
GEVulkanDriver* vk = getVKDriver();
// m_env_descriptor_layout
std::array<VkDescriptorSetLayoutBinding, 4> texture_layout_binding = {};
texture_layout_binding[0].binding = 0;
texture_layout_binding[0].descriptorCount = 1;
texture_layout_binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
texture_layout_binding[0].pImmutableSamplers = NULL;
texture_layout_binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_layout_binding[1] = texture_layout_binding[0];
texture_layout_binding[1].binding = 1;
texture_layout_binding[2] = texture_layout_binding[0];
texture_layout_binding[2].binding = 2;
texture_layout_binding[3] = texture_layout_binding[0];
texture_layout_binding[3].binding = 3;
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.flags = 0;
setinfo.pNext = NULL;
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = texture_layout_binding.data();
setinfo.bindingCount = texture_layout_binding.size();
if (vkCreateDescriptorSetLayout(vk->getDevice(), &setinfo,
NULL, &m_env_descriptor_layout) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorSetLayout failed for "
"GEVulkanSkyBoxRenderer::m_env_descriptor_layout");
}
// m_descriptor_pool
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptorCount =
texture_layout_binding.size() * m_env_descriptor_set.size();
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = m_env_descriptor_set.size();
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(vk->getDevice(), &pool_info, NULL,
&m_descriptor_pool) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"GEVulkanSkyBoxRenderer");
}
// m_env_descriptor_set
std::vector<VkDescriptorSetLayout> layouts(2, m_env_descriptor_layout);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(vk->getDevice(), &alloc_info,
m_env_descriptor_set.data()) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"GEVulkanSkyBoxRenderer::m_env_descriptor_set");
}
std::array<VkDescriptorImageInfo, texture_layout_binding.size()> info;
info[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
info[0].sampler = vk->getSampler(GVS_SKYBOX);
info[0].imageView = (VkImageView)m_dummy_env_cubemap->getTextureHandler();
info[1] = info[0];
info[2] = info[0];
info[3] = info[0];
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = info.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_env_descriptor_set[0];
write_descriptor_set.pImageInfo = info.data();
vkUpdateDescriptorSets(vk->getDevice(), 1, &write_descriptor_set, 0, NULL);
} // init
// ----------------------------------------------------------------------------
GEVulkanSkyBoxRenderer::~GEVulkanSkyBoxRenderer()
{
GEVulkanDriver* vk = getVKDriver();
if (!vk)
return;
vk->waitIdle();
while (m_skybox_loading.load());
while (m_env_cubemap_loading.load());
if (m_texture_cubemap != NULL)
m_texture_cubemap->drop();
if (m_diffuse_env_cubemap != NULL)
m_diffuse_env_cubemap->drop();
if (m_specular_env_cubemap != NULL)
m_specular_env_cubemap->drop();
if (m_dummy_env_cubemap != NULL)
m_dummy_env_cubemap->drop();
if (m_descriptor_pool != VK_NULL_HANDLE)
vkDestroyDescriptorPool(vk->getDevice(), m_descriptor_pool, NULL);
if (m_env_descriptor_layout != VK_NULL_HANDLE)
{
vkDestroyDescriptorSetLayout(vk->getDevice(), m_env_descriptor_layout,
NULL);
}
} // ~GEVulkanSkyBoxRenderer
// ----------------------------------------------------------------------------
void GEVulkanSkyBoxRenderer::addSkyBox(irr::scene::ISceneNode* skybox)
{
if (skybox->getType() != irr::scene::ESNT_SKY_BOX)
return;
if (m_skybox == skybox)
return;
while (m_skybox_loading.load());
while (m_env_cubemap_loading.load());
m_skybox = skybox;
std::vector<GEVulkanTexture*> sky_tex;
std::array<int, 6> order = {{ 1, 3, 4, 5, 2, 0}};
for (unsigned i = 0; i < 6; i++)
{
video::ITexture* tex = skybox->getMaterial(order[i]).getTexture(0);
if (!tex)
return;
sky_tex.push_back(static_cast<GEVulkanTexture*>(tex));
}
class ImageManipulator
{
private:
GEVulkanSkyBoxRenderer* m_sky;
// ----------------------------------------------------------------
void updateDescriptor()
{
GEVulkanDriver* vk = getVKDriver();
std::array<VkDescriptorImageInfo, 4> info;
info[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
info[0].sampler = vk->getSampler(GVS_SKYBOX);
info[0].imageView = m_sky->m_diffuse_env_cubemap ?
(VkImageView)m_sky->m_diffuse_env_cubemap
->getTextureHandler() :
(VkImageView)m_sky->m_dummy_env_cubemap
->getTextureHandler();
info[1] = info[0];
info[1].imageView = m_sky->m_specular_env_cubemap ?
(VkImageView)m_sky->m_specular_env_cubemap
->getTextureHandler() :
(VkImageView)m_sky->m_dummy_env_cubemap
->getTextureHandler();
info[2] = info[0];
info[2].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info[2].imageView = (VkImageView)m_sky->m_texture_cubemap
->getImageView(false/*srgb*/)->load();
info[3] = info[2];
info[3].imageView = (VkImageView)m_sky->m_texture_cubemap
->getImageView(true/*srgb*/)->load();
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType =
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = 0;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = info.size();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_sky->m_env_descriptor_set[1];
write_descriptor_set.pImageInfo = info.data();
vkUpdateDescriptorSets(vk->getDevice(), 1,
&write_descriptor_set, 0, NULL);
m_sky->m_skybox_loading.store(false);
}
public:
// ----------------------------------------------------------------
ImageManipulator(GEVulkanSkyBoxRenderer* sky) : m_sky(sky)
{
m_sky->m_skybox_loading.store(true);
}
// ----------------------------------------------------------------
~ImageManipulator()
{
if (m_sky->m_diffuse_env_cubemap != NULL)
{
GEVulkanEnvironmentMap env(m_sky);
updateDescriptor();
env.load();
}
else
updateDescriptor();
}
// ----------------------------------------------------------------
void swapPixels(video::IImage* img, unsigned idx)
{
if (!(idx == 2 || idx == 3))
return;
if (idx == 2)
{
video::IImage* pixel = getDriver()->createImage(
video::ECF_A8R8G8B8, core::dimension2du(1, 1));
img->copyToScaling(pixel);
m_sky->m_skytop_color.store(*(uint32_t*)pixel->lock());
pixel->drop();
}
unsigned width = img->getDimension().Width;
uint8_t* tmp = new uint8_t[width * width * 4];
uint32_t* tmp_array = (uint32_t*)tmp;
uint32_t* img_data = (uint32_t*)img->lock();
for (unsigned i = 0; i < width; i++)
{
for (unsigned j = 0; j < width; j++)
{
tmp_array[j * width + i] =
img_data[i * width + (width - j - 1)];
}
}
uint8_t* u8_data = (uint8_t*)img->lock();
delete [] u8_data;
img->setMemory(tmp);
}
};
GEVulkanDriver* vk = getVKDriver();
std::shared_ptr<ImageManipulator> image_manipulator =
std::make_shared<ImageManipulator>(this);
auto real_mani = [image_manipulator](video::IImage* img, unsigned idx)
{
image_manipulator->swapPixels(img, idx);
};
if (getGEConfig()->m_pbr && getGEConfig()->m_ibl &&
GEVulkanFeatures::supportsComputeInMainQueue())
{
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM;
if (GEVulkanFeatures::supportsShaderStorageImageExtendedFormats())
format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
if (m_diffuse_env_cubemap == NULL)
{
m_diffuse_env_cubemap =
new GEVulkanArrayTexture(format, VK_IMAGE_VIEW_TYPE_CUBE,
GEVulkanEnvironmentMap::getDiffuseEnvironmentMapSize(), 6,
video::SColor(0));
}
if (m_specular_env_cubemap == NULL)
{
m_specular_env_cubemap =
new GEVulkanArrayTexture(format, VK_IMAGE_VIEW_TYPE_CUBE,
GEVulkanEnvironmentMap::getSpecularEnvironmentMapSize(), 6,
video::SColor(0));
}
}
else
{
if (m_diffuse_env_cubemap != NULL)
{
m_diffuse_env_cubemap->drop();
m_diffuse_env_cubemap = NULL;
}
if (m_specular_env_cubemap != NULL)
{
m_specular_env_cubemap->drop();
m_specular_env_cubemap = NULL;
}
}
if (m_texture_cubemap)
m_texture_cubemap->drop();
m_texture_cubemap = new GEVulkanArrayTexture(sky_tex,
VK_IMAGE_VIEW_TYPE_CUBE, real_mani);
} // addSkyBox
// ----------------------------------------------------------------------------
const VkDescriptorSet* GEVulkanSkyBoxRenderer::getEnvDescriptorSet() const
{
if (m_skybox == NULL || m_skybox_loading.load() == true ||
m_env_cubemap_loading.load() == true)
return &m_env_descriptor_set[0];
return &m_env_descriptor_set[1];
} // getEnvDescriptorSet
}
@@ -0,0 +1,70 @@
#ifndef HEADER_GE_VULKAN_SKYBOX_RENDERER_HPP
#define HEADER_GE_VULKAN_SKYBOX_RENDERER_HPP
#include "vulkan_wrapper.h"
#include <SColor.h>
#include <array>
#include <atomic>
namespace irr
{
namespace scene { class ISceneNode; }
}
namespace GE
{
class GEVulkanArrayTexture;
class GEVulkanEnvironmentMap;
class GEVulkanSkyBoxRenderer
{
private:
friend class GEVulkanEnvironmentMap;
irr::scene::ISceneNode* m_skybox;
GEVulkanArrayTexture *m_texture_cubemap, *m_diffuse_env_cubemap,
*m_specular_env_cubemap, *m_dummy_env_cubemap;
VkDescriptorSetLayout m_env_descriptor_layout;
VkDescriptorPool m_descriptor_pool;
std::array<VkDescriptorSet, 2> m_env_descriptor_set;
std::atomic_bool m_skybox_loading, m_env_cubemap_loading;
std::atomic<uint32_t> m_skytop_color;
public:
// ------------------------------------------------------------------------
GEVulkanSkyBoxRenderer();
// ------------------------------------------------------------------------
~GEVulkanSkyBoxRenderer();
// ------------------------------------------------------------------------
void addSkyBox(irr::scene::ISceneNode* node);
// ------------------------------------------------------------------------
VkDescriptorSetLayout getEnvDescriptorSetLayout() const
{ return m_env_descriptor_layout; }
// ------------------------------------------------------------------------
const VkDescriptorSet* getEnvDescriptorSet() const;
// ------------------------------------------------------------------------
void reset()
{
while (m_skybox_loading.load());
while (m_env_cubemap_loading.load());
m_skybox = NULL;
}
// ------------------------------------------------------------------------
irr::video::SColor getSkytopColor() const
{
irr::video::SColor c(0);
if (m_skybox_loading.load() == true)
return c;
c.color = m_skytop_color.load();
return c;
}
}; // GEVulkanSkyBoxRenderer
}
#endif
@@ -0,0 +1,956 @@
#include "ge_vulkan_texture.hpp"
#include "ge_main.hpp"
#include "ge_mipmap_generator.hpp"
#include "ge_compressor_astc_4x4.hpp"
#include "ge_compressor_bptc_bc7.hpp"
#include "ge_compressor_s3tc_bc3.hpp"
#include "ge_texture.hpp"
#include "ge_vulkan_command_loader.hpp"
#include "ge_vulkan_features.hpp"
#include "ge_vulkan_driver.hpp"
extern "C"
{
#include <mipmap/img.h>
#include <mipmap/imgresize.h>
}
#include <cassert>
#include <cstdio>
#include <IAttributes.h>
#include <IImageLoader.h>
#include <limits>
#include <stdexcept>
namespace GE
{
GEVulkanTexture::GEVulkanTexture(const std::string& path,
std::function<void(video::IImage*)> image_mani)
: video::ITexture(path.c_str()), m_image_mani(image_mani),
m_locked_data(NULL),
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_vma_info(), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(false), m_has_mipmaps(true),
m_ondemand_load(false), m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
core::dimension2du max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
m_full_path = getDriver()->getFileSystem()->getAbsolutePath(NamedPath);
if (!getDriver()->getFileSystem()->existFileOnly(m_full_path))
{
LoadingFailed = true;
return;
}
auto& paths = getGEConfig()->m_ondemand_load_texture_paths;
auto path_itr = paths.find(m_full_path.c_str());
m_ondemand_load = (path_itr != paths.end());
if (m_ondemand_load)
{
paths.erase(path_itr);
video::IImageLoader* loader = NULL;
io::IReadFile* file = io::createReadFile(m_full_path);
getDriver()->createImageFromFile(file, &loader);
if (loader && loader->getImageSize(file, &m_orig_size))
{
m_size = getResizingTarget(m_orig_size, max_size);
if (m_size.Width < 4 || m_size.Height < 4)
m_has_mipmaps = false;
setPlaceHolderView();
}
else
LoadingFailed = true;
file->drop();
return;
}
m_size_lock.lock();
m_image_view_lock.lock();
m_thread_loading_lock.lock();
GEVulkanCommandLoader::addMultiThreadingCommand(
std::bind(&GEVulkanTexture::reloadInternal, this, max_size));
} // GEVulkanTexture
// ----------------------------------------------------------------------------
GEVulkanTexture::GEVulkanTexture(video::IImage* img, const std::string& name)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_locked_data(NULL),
m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_vma_info(), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(true), m_has_mipmaps(true),
m_ondemand_load(false), m_ondemand_loading(false),
m_internal_format(VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
if (!img)
{
LoadingFailed = true;
return;
}
m_size = m_orig_size = img->getDimension();
if (m_size.Width < 4 || m_size.Height < 4)
m_has_mipmaps = false;
uint8_t* data = (uint8_t*)img->lock();
bgraConversion(data);
upload(data, m_has_mipmaps/*generate_hq_mipmap*/);
img->unlock();
img->drop();
} // GEVulkanTexture
// ----------------------------------------------------------------------------
GEVulkanTexture::GEVulkanTexture(const std::string& name, unsigned int size,
bool single_channel)
: video::ITexture(name.c_str()), m_image_mani(nullptr),
m_locked_data(NULL), m_vulkan_device(getVKDriver()->getDevice()),
m_image(VK_NULL_HANDLE), m_vma_allocation(VK_NULL_HANDLE),
m_vma_info(), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D), m_disable_reload(true),
m_has_mipmaps(true), m_ondemand_load(false),
m_ondemand_loading(false), m_internal_format(single_channel ?
VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM),
m_vk(getVKDriver())
{
if (isSingleChannel() && !GEVulkanFeatures::supportsR8Blit())
m_has_mipmaps = false;
else if (!isSingleChannel() && !GEVulkanFeatures::supportsRGBA8Blit())
m_has_mipmaps = false;
m_orig_size.Width = size;
m_orig_size.Height = size;
m_size = m_orig_size;
std::vector<uint8_t> data;
data.resize(size * size * (isSingleChannel() ? 1 : 4), 0);
upload(data.data());
} // GEVulkanTexture
// ----------------------------------------------------------------------------
GEVulkanTexture::~GEVulkanTexture()
{
m_thread_loading_lock.lock();
m_thread_loading_lock.unlock();
if (m_image_view || m_image != VK_NULL_HANDLE ||
m_vma_allocation != VK_NULL_HANDLE)
m_vk->waitIdle();
clearVulkanData();
} // ~GEVulkanTexture
// ----------------------------------------------------------------------------
bool GEVulkanTexture::createTextureImage(uint8_t* texture_data,
bool generate_hq_mipmap)
{
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)
{
const bool normal_map = (std::string(NamedPath.getPtr()).find(
"_Normal.") != std::string::npos);
bool texture_compression = getGEConfig()->m_texture_compression;
if (texture_compression && GEVulkanFeatures::supportsASTC4x4())
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
mipmap_generator = new GECompressorASTC4x4(texture_data, channels,
m_size, normal_map);
}
else if (texture_compression && GEVulkanFeatures::supportsBPTCBC7())
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_BC7_UNORM_BLOCK;
mipmap_generator = new GECompressorBPTCBC7(texture_data, channels,
m_size, normal_map);
}
else if (texture_compression && GEVulkanFeatures::supportsS3TCBC3())
{
image_size = get4x4CompressedTextureSize(m_size.Width,
m_size.Height);
m_internal_format = VK_FORMAT_BC3_UNORM_BLOCK;
mipmap_generator = new GECompressorS3TCBC3(texture_data, channels,
m_size, normal_map);
}
else
{
m_internal_format = (isSingleChannel() ?
VK_FORMAT_R8_UNORM : VK_FORMAT_R8G8B8A8_UNORM);
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;
VkBuffer staging_buffer;
VmaAllocation staging_buffer_allocation;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
staging_buffer_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
bool success = m_vk->createBuffer(image_total_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation);
if (!success)
return false;
VkResult ret = VK_SUCCESS;
uint8_t* data;
VkCommandBuffer command_buffer = VK_NULL_HANDLE;
if ((ret = vmaMapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation,
(void**)&data)) != VK_SUCCESS)
goto destroy;
if (mipmap_generator)
{
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);
success = createImage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
if (!success)
{
ret = VK_NOT_READY;
goto destroy;
}
command_buffer = GEVulkanCommandLoader::beginSingleTimeCommands();
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
if (mipmap_generator)
{
unsigned offset = 0;
std::vector<GEImageLevel>& levels = mipmap_generator->getAllLevels();
for (unsigned i = 0; i < levels.size(); i++)
{
GEImageLevel& level = levels[i];
copyBufferToImage(command_buffer, staging_buffer,
level.m_dim.Width, level.m_dim.Height, 0, 0, offset, i, 0);
offset += level.m_size;
}
}
else
{
copyBufferToImage(command_buffer, staging_buffer, m_size.Width,
m_size.Height, 0, 0, 0, 0, 0);
}
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
destroy:
delete mipmap_generator;
vmaDestroyBuffer(m_vk->getVmaAllocator(), staging_buffer,
staging_buffer_allocation);
return ret == VK_SUCCESS;
} // createTextureImage
// ----------------------------------------------------------------------------
bool GEVulkanTexture::createImage(VkImageUsageFlags usage)
{
VkImageCreateInfo image_info = {};
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
image_info.imageType = VK_IMAGE_TYPE_2D;
image_info.extent.width = m_size.Width;
image_info.extent.height = m_size.Height;
image_info.extent.depth = 1;
image_info.mipLevels = getMipmapLevels();
image_info.arrayLayers = m_layer_count;
image_info.format = m_internal_format;
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
image_info.usage = usage;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE ||
m_image_view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
image_info.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
if (m_internal_format != getSRGBformat(m_internal_format))
image_info.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
m_vma_info = {};
VmaAllocationCreateInfo alloc_info = {};
alloc_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
if ((usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0)
alloc_info.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
VkResult result = vmaCreateImage(m_vk->getVmaAllocator(), &image_info,
&alloc_info, &m_image, &m_vma_allocation, &m_vma_info);
if (result != VK_SUCCESS)
return false;
return true;
} // createImage
// ----------------------------------------------------------------------------
void GEVulkanTexture::transitionImageLayout(VkCommandBuffer command_buffer,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_image;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = getMipmapLevels();
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = m_layer_count;
if (new_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
}
else
{
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
VkPipelineStageFlags source_stage;
VkPipelineStageFlags destination_stage;
if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destination_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL &&
new_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destination_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL &&
new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL &&
new_layout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
destination_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
new_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destination_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
new_layout == VK_IMAGE_LAYOUT_GENERAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_UNDEFINED &&
new_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else if (old_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
new_layout == VK_IMAGE_LAYOUT_GENERAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destination_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else
{
return;
}
vkCmdPipelineBarrier(command_buffer, source_stage, destination_stage, 0, 0,
NULL, 0, NULL, 1, &barrier);
} // transitionImageLayout
// ----------------------------------------------------------------------------
void GEVulkanTexture::copyBufferToImage(VkCommandBuffer command_buffer,
VkBuffer buffer, u32 w, u32 h, s32 x,
s32 y, u32 offset, u32 mipmap_level,
u32 layer_level)
{
VkBufferImageCopy region = {};
region.bufferOffset = offset;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = mipmap_level;
region.imageSubresource.baseArrayLayer = layer_level;
region.imageSubresource.layerCount = 1;
region.imageOffset = {x, y, 0};
region.imageExtent = {w, h, 1};
vkCmdCopyBufferToImage(command_buffer, buffer, m_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
} // copyBufferToImage
// ----------------------------------------------------------------------------
bool GEVulkanTexture::createImageView(VkImageAspectFlags aspect_flags,
bool create_srgb_view)
{
VkImageViewCreateInfo view_info = {};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = m_image;
view_info.viewType = m_image_view_type;
view_info.format = m_internal_format;
view_info.subresourceRange.aspectMask = aspect_flags;
view_info.subresourceRange.baseMipLevel = 0;
view_info.subresourceRange.levelCount = getMipmapLevels();
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = m_layer_count;
if (isSingleChannel())
{
view_info.components.r = VK_COMPONENT_SWIZZLE_ONE;
view_info.components.g = VK_COMPONENT_SWIZZLE_ONE;
view_info.components.b = VK_COMPONENT_SWIZZLE_ONE;
view_info.components.a = VK_COMPONENT_SWIZZLE_R;
}
auto image_view = std::make_shared<std::atomic<VkImageView> >();
VkImageView view_ptr = VK_NULL_HANDLE;
VkResult result = vkCreateImageView(m_vulkan_device, &view_info, NULL,
&view_ptr);
if (result == VK_SUCCESS)
{
image_view.get()->store(view_ptr);
m_image_view = image_view;
VkFormat srgb_format = getSRGBformat(m_internal_format);
if (create_srgb_view && m_internal_format != srgb_format)
{
image_view = std::make_shared<std::atomic<VkImageView> >();
view_info.format = srgb_format;
view_ptr = VK_NULL_HANDLE;
if (vkCreateImageView(m_vulkan_device, &view_info,
NULL, &view_ptr) == VK_SUCCESS)
{
image_view.get()->store(view_ptr);
m_image_view_srgb = image_view;
}
}
if (m_placeholder_view)
m_placeholder_view.get()->store(VK_NULL_HANDLE);
m_ondemand_loading.store(false);
return true;
}
else
{
m_ondemand_loading.store(false);
return false;
}
} // createImageView
// ----------------------------------------------------------------------------
void GEVulkanTexture::clearVulkanData()
{
if (m_image_view)
{
vkDestroyImageView(m_vulkan_device, m_image_view.get()->load(), NULL);
m_image_view.get()->store(VK_NULL_HANDLE);
m_image_view.reset();
if (m_image_view_srgb)
{
vkDestroyImageView(m_vulkan_device,
m_image_view_srgb.get()->load(), NULL);
m_image_view_srgb.get()->store(VK_NULL_HANDLE);
m_image_view_srgb.reset();
}
}
if (m_image != VK_NULL_HANDLE)
{
vmaDestroyImage(m_vk->getVmaAllocator(), m_image, m_vma_allocation);
m_image = VK_NULL_HANDLE;
m_vma_allocation = VK_NULL_HANDLE;
m_vma_info = {};
}
} // clearVulkanData
// ----------------------------------------------------------------------------
void GEVulkanTexture::reloadInternal(const core::dimension2du& max_size)
{
if (m_disable_reload)
return;
clearVulkanData();
video::IImage* texture_image = getResizedImageFullPath(m_full_path,
max_size, &m_orig_size);
if (texture_image == NULL)
{
if (m_ondemand_load)
{
printf("Missing texture_image in getResizedImageFullPath when "
"reloadInternal during ondemand loading for %s\n",
m_full_path.c_str());
m_size_lock.unlock();
m_image_view_lock.unlock();
m_thread_loading_lock.unlock();
return;
}
else
{
throw std::runtime_error(
"Missing texture_image in getResizedImageFullPath");
}
}
m_size = texture_image->getDimension();
if (m_size.Width < 4 || m_size.Height < 4)
m_has_mipmaps = false;
else
m_has_mipmaps = true;
m_size_lock.unlock();
if (m_image_mani)
m_image_mani(texture_image);
uint8_t* data = (uint8_t*)texture_image->lock();
bgraConversion(data);
upload(data, m_has_mipmaps/*generate_hq_mipmap*/);
m_image_view_lock.unlock();
texture_image->unlock();
texture_image->drop();
m_thread_loading_lock.unlock();
} // reloadInternal
// ----------------------------------------------------------------------------
void GEVulkanTexture::upload(uint8_t* data, bool generate_hq_mipmap)
{
if (!createTextureImage(data, generate_hq_mipmap))
{
m_ondemand_loading.store(false);
return;
}
if (!createImageView(VK_IMAGE_ASPECT_COLOR_BIT))
return;
} // upload
// ----------------------------------------------------------------------------
void* GEVulkanTexture::lock(video::E_TEXTURE_LOCK_MODE mode, u32 mipmap_level)
{
uint8_t* texture_data = getTextureData();
if (!texture_data)
return NULL;
if (isSingleChannel())
{
m_locked_data = new uint8_t[m_size.Width * m_size.Height * 4]();
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
{
m_locked_data[i * 4 + 2] = texture_data[i];
m_locked_data[i * 4 + 3] = 255;
}
delete [] texture_data;
return m_locked_data;
}
else
{
m_locked_data = texture_data;
bgraConversion(m_locked_data);
return m_locked_data;
}
} // lock
// ----------------------------------------------------------------------------
uint8_t* GEVulkanTexture::getTextureData()
{
if (m_internal_format != VK_FORMAT_R8G8B8A8_UNORM &&
m_internal_format != VK_FORMAT_R8_UNORM)
{
if (m_full_path.empty())
return NULL;
const core::dimension2du& max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
video::IImage* texture_image = getResizedImageFullPath(m_full_path,
max_size, NULL, &m_size);
if (texture_image == NULL)
return NULL;
texture_image->setDeleteMemory(false);
uint8_t* data = (uint8_t*)texture_image->lock();
texture_image->drop();
return data;
}
if (!waitImageView())
return NULL;
VkBuffer buffer;
VmaAllocation buffer_allocation;
VkDeviceSize image_size =
m_size.Width * m_size.Height * (isSingleChannel() ? 1 : 4);
VmaAllocationCreateInfo buffer_create_info = {};
buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO;
buffer_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (!m_vk->createBuffer(image_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT, buffer_create_info, buffer,
buffer_allocation))
return NULL;
VkCommandBuffer command_buffer = GEVulkanCommandLoader::beginSingleTimeCommands();
transitionImageLayout(command_buffer,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = {m_size.Width, m_size.Height, 1};
vkCmdCopyImageToBuffer(command_buffer, m_image,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1, &region);
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
uint8_t* texture_data = new uint8_t[image_size];
void* mapped_data;
if (vmaMapMemory(m_vk->getVmaAllocator(), buffer_allocation,
&mapped_data) != VK_SUCCESS)
{
delete [] texture_data;
texture_data = NULL;
goto cleanup;
}
vmaInvalidateAllocation(m_vk->getVmaAllocator(), buffer_allocation,
0, image_size);
memcpy(texture_data, mapped_data, image_size);
vmaUnmapMemory(m_vk->getVmaAllocator(), buffer_allocation);
cleanup:
vmaDestroyBuffer(m_vk->getVmaAllocator(), buffer, buffer_allocation);
return texture_data;
} // getTextureData
//-----------------------------------------------------------------------------
void GEVulkanTexture::updateTexture(void* data, video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y)
{
if (!waitImageView())
return;
VkBuffer staging_buffer;
VmaAllocation staging_buffer_allocation;
VmaAllocationCreateInfo staging_buffer_create_info = {};
staging_buffer_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
staging_buffer_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
staging_buffer_create_info.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (isSingleChannel())
{
if (format == video::ECF_R8)
{
unsigned image_size = w * h;
if (!m_vk->createBuffer(image_size,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation))
return;
void* mapped_data;
vmaMapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation,
&mapped_data);
memcpy(mapped_data, data, (size_t)(image_size));
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation);
vmaFlushAllocation(m_vk->getVmaAllocator(),
staging_buffer_allocation, 0, image_size);
}
}
else
{
if (format == video::ECF_R8)
{
unsigned image_size = w * h * 4;
if (!m_vk->createBuffer(w * h * 4,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation))
return;
const unsigned int size = w * h;
std::vector<uint8_t> image_data(size * 4, 255);
uint8_t* orig_data = (uint8_t*)data;
for (unsigned int i = 0; i < size; i++)
image_data[4 * i + 3] = orig_data[i];
void* mapped_data;
vmaMapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation,
&mapped_data);
memcpy(mapped_data, image_data.data(), (size_t)(image_size));
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation);
vmaFlushAllocation(m_vk->getVmaAllocator(),
staging_buffer_allocation, 0, image_size);
}
else if (format == video::ECF_A8R8G8B8)
{
unsigned image_size = w * h * 4;
if (!m_vk->createBuffer(w * h * 4,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, staging_buffer_create_info,
staging_buffer, staging_buffer_allocation))
return;
uint8_t* u8_data = (uint8_t*)data;
for (unsigned int i = 0; i < w * h; i++)
{
uint8_t tmp_val = u8_data[i * 4];
u8_data[i * 4] = u8_data[i * 4 + 2];
u8_data[i * 4 + 2] = tmp_val;
}
void* mapped_data;
vmaMapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation,
&mapped_data);
memcpy(mapped_data, u8_data, (size_t)(image_size));
vmaUnmapMemory(m_vk->getVmaAllocator(), staging_buffer_allocation);
vmaFlushAllocation(m_vk->getVmaAllocator(),
staging_buffer_allocation, 0, image_size);
}
}
VkCommandBuffer command_buffer = GEVulkanCommandLoader::beginSingleTimeCommands();
transitionImageLayout(command_buffer,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyBufferToImage(command_buffer, staging_buffer, w, h, x, y, 0, 0, 0);
bool blit_mipmap = true;
if (isSingleChannel() && !GEVulkanFeatures::supportsR8Blit())
blit_mipmap = false;
else if (!isSingleChannel() && !GEVulkanFeatures::supportsRGBA8Blit())
blit_mipmap = false;
if (blit_mipmap)
{
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.image = m_image;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
int mip_width = m_size.Width;
int mip_height = m_size.Height;
unsigned mip_levels = getMipmapLevels();
for (unsigned i = 1; i < mip_levels; i++)
{
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
vkCmdPipelineBarrier(command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, NULL, 0, NULL, 1, &barrier);
VkImageBlit blit{};
blit.srcOffsets[0] = {0, 0, 0};
blit.srcOffsets[1] = {mip_width, mip_height, 1};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.dstOffsets[0] = {0, 0, 0};
blit.dstOffsets[1] =
{
mip_width > 1 ? mip_width / 2 : 1,
mip_height > 1 ? mip_height / 2 : 1,
1
};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
vkCmdBlitImage(command_buffer,
m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
VK_FILTER_LINEAR);
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
vkCmdPipelineBarrier(command_buffer,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
&barrier);
if (mip_width > 1) mip_width /= 2;
if (mip_height > 1) mip_height /= 2;
}
}
transitionImageLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
GEVulkanCommandLoader::endSingleTimeCommands(command_buffer);
vmaDestroyBuffer(m_vk->getVmaAllocator(), staging_buffer,
staging_buffer_allocation);
} // updateTexture
//-----------------------------------------------------------------------------
void GEVulkanTexture::bgraConversion(uint8_t* img_data)
{
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
{
uint8_t tmp_val = img_data[i * 4];
img_data[i * 4] = img_data[i * 4 + 2];
img_data[i * 4 + 2] = tmp_val;
}
} // bgraConversion
//-----------------------------------------------------------------------------
void GEVulkanTexture::reload()
{
// Copied from waitImageView
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
}
else
{
bool is_currently_loading = m_ondemand_loading.load();
if (is_currently_loading || m_image == VK_NULL_HANDLE)
return;
}
if (m_image_view || m_image != VK_NULL_HANDLE ||
m_vma_allocation != VK_NULL_HANDLE)
m_vk->waitIdle();
if (m_ondemand_load)
{
clearVulkanData();
setPlaceHolderView();
}
else if (!m_disable_reload)
{
core::dimension2du max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
m_size_lock.lock();
m_image_view_lock.lock();
m_thread_loading_lock.lock();
GEVulkanCommandLoader::addMultiThreadingCommand(
std::bind(&GEVulkanTexture::reloadInternal, this, max_size));
}
} // reload
//-----------------------------------------------------------------------------
void GEVulkanTexture::setPlaceHolderView()
{
auto tex = static_cast<GEVulkanTexture*>(m_vk->getTransparentTexture());
auto image_view = std::make_shared<std::atomic<VkImageView> >();
image_view.get()->store((VkImageView)tex->getTextureHandler());
if (m_placeholder_view)
m_placeholder_view.get()->store(VK_NULL_HANDLE);
m_placeholder_view = image_view;
} // setPlaceHolderView
//-----------------------------------------------------------------------------
std::shared_ptr<std::atomic<VkImageView> > GEVulkanTexture::getImageViewLive(
bool srgb) const
{
assert(m_ondemand_load && m_placeholder_view);
if (m_ondemand_loading.load() == false)
{
if (m_image_view)
{
if (srgb && m_image_view_srgb)
return m_image_view_srgb;
else
return m_image_view;
}
else
{
GEVulkanTexture* tex = const_cast<GEVulkanTexture*>(this);
core::dimension2du max_size = getDriver()->getDriverAttributes()
.getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
tex->m_thread_loading_lock.lock();
tex->m_ondemand_loading.store(true);
GEVulkanCommandLoader::addMultiThreadingCommand(
std::bind(&GEVulkanTexture::reloadInternal, tex, max_size));
return m_placeholder_view;
}
}
return m_placeholder_view;
} // getImageViewLive
}
@@ -0,0 +1,260 @@
#ifndef HEADER_GE_VULKAN_TEXTURE_HPP
#define HEADER_GE_VULKAN_TEXTURE_HPP
#include "vulkan_wrapper.h"
#include "ge_vma.hpp"
#include "ge_spin_lock.hpp"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <ITexture.h>
using namespace irr;
namespace GE
{
class GEVulkanDriver;
class GEVulkanDeferredFBO;
class GEVulkanTexture : public video::ITexture
{
protected:
friend class GEVulkanDeferredFBO;
core::dimension2d<u32> m_size, m_orig_size;
std::function<void(video::IImage*)> m_image_mani;
uint8_t* m_locked_data;
VkDevice m_vulkan_device;
VkImage m_image;
VmaAllocation m_vma_allocation;
VmaAllocationInfo m_vma_info;
std::shared_ptr<std::atomic<VkImageView> > m_image_view;
std::shared_ptr<std::atomic<VkImageView> > m_image_view_srgb;
std::shared_ptr<std::atomic<VkImageView> > m_placeholder_view;
unsigned m_layer_count;
VkImageViewType m_image_view_type;
const bool m_disable_reload;
bool m_has_mipmaps;
bool m_ondemand_load;
mutable std::atomic<bool> m_ondemand_loading;
GESpinLock m_size_lock;
mutable GESpinLock m_image_view_lock;
GESpinLock m_thread_loading_lock;
io::path m_full_path;
VkFormat m_internal_format;
GEVulkanDriver* m_vk;
// ------------------------------------------------------------------------
VkFormat getSRGBformat(VkFormat format)
{
if (format == VK_FORMAT_R8G8B8A8_UNORM)
return VK_FORMAT_R8G8B8A8_SRGB;
else if (format == VK_FORMAT_ASTC_4x4_UNORM_BLOCK)
return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;
else if (format == VK_FORMAT_BC7_UNORM_BLOCK)
return VK_FORMAT_BC7_SRGB_BLOCK;
else if (format == VK_FORMAT_BC3_UNORM_BLOCK)
return VK_FORMAT_BC3_SRGB_BLOCK;
return format;
}
// ------------------------------------------------------------------------
bool createTextureImage(uint8_t* texture_data, bool generate_hq_mipmap);
// ------------------------------------------------------------------------
bool createImage(VkImageUsageFlags usage);
// ------------------------------------------------------------------------
bool createImageView(VkImageAspectFlags aspect_flags,
bool create_srgb_view = true);
// ------------------------------------------------------------------------
void transitionImageLayout(VkCommandBuffer command_buffer,
VkImageLayout old_layout,
VkImageLayout new_layout);
// ------------------------------------------------------------------------
void copyBufferToImage(VkCommandBuffer command_buffer, VkBuffer buffer,
u32 w, u32 h, s32 x, s32 y, u32 offset,
u32 mipmap_level, u32 layer_level);
// ------------------------------------------------------------------------
void upload(uint8_t* data, bool generate_hq_mipmap = false);
// ------------------------------------------------------------------------
void clearVulkanData();
// ------------------------------------------------------------------------
void reloadInternal(const core::dimension2du& max_size);
// ------------------------------------------------------------------------
void bgraConversion(uint8_t* img_data);
// ------------------------------------------------------------------------
uint8_t* getTextureData();
// ------------------------------------------------------------------------
bool isSingleChannel() const
{ return m_internal_format == VK_FORMAT_R8_UNORM; }
// ------------------------------------------------------------------------
void setPlaceHolderView();
// ------------------------------------------------------------------------
std::shared_ptr<std::atomic<VkImageView> > getImageViewLive(
bool srgb = false) const;
// ------------------------------------------------------------------------
bool waitImageView() const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
}
else
{
while (m_ondemand_loading.load());
if (m_image == VK_NULL_HANDLE)
return false;
}
return true;
}
// ------------------------------------------------------------------------
GEVulkanTexture() : video::ITexture(""), m_vma_info(), m_layer_count(1),
m_image_view_type(VK_IMAGE_VIEW_TYPE_2D),
m_disable_reload(true), m_ondemand_load(false),
m_ondemand_loading(false) {}
public:
// ------------------------------------------------------------------------
GEVulkanTexture(const std::string& path,
std::function<void(video::IImage*)> image_mani = nullptr);
// ------------------------------------------------------------------------
GEVulkanTexture(video::IImage* img, const std::string& name);
// ------------------------------------------------------------------------
GEVulkanTexture(const std::string& name, unsigned int size,
bool single_channel);
// ------------------------------------------------------------------------
virtual ~GEVulkanTexture();
// ------------------------------------------------------------------------
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
video::ETLM_READ_WRITE, u32 mipmap_level = 0);
// ------------------------------------------------------------------------
virtual void unlock()
{
if (m_locked_data)
{
delete [] m_locked_data;
m_locked_data = NULL;
}
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getOriginalSize() const
{
if (!m_ondemand_load)
{
m_size_lock.lock();
m_size_lock.unlock();
}
return m_orig_size;
}
// ------------------------------------------------------------------------
virtual const core::dimension2d<u32>& getSize() const
{
if (!m_ondemand_load)
{
m_size_lock.lock();
m_size_lock.unlock();
}
return m_size;
}
// ------------------------------------------------------------------------
virtual video::E_DRIVER_TYPE getDriverType() const
{ return video::EDT_VULKAN; }
// ------------------------------------------------------------------------
virtual video::ECOLOR_FORMAT getColorFormat() const
{ return video::ECF_A8R8G8B8; }
// ------------------------------------------------------------------------
virtual u32 getPitch() const { return 0; }
// ------------------------------------------------------------------------
virtual bool hasMipMaps() const { return m_has_mipmaps; }
// ------------------------------------------------------------------------
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
// ------------------------------------------------------------------------
virtual u64 getTextureHandler() const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
return m_image_view ? (u64)(m_image_view.get()->load()) : 0;
}
else
{
auto image_view = getImageViewLive();
return (u64)(image_view.get()->load());
}
}
// ------------------------------------------------------------------------
virtual unsigned int getTextureSize() const
{
waitImageView();
return (unsigned int)m_vma_info.size;
}
// ------------------------------------------------------------------------
virtual void reload();
// ------------------------------------------------------------------------
virtual void updateTexture(void* data, irr::video::ECOLOR_FORMAT format,
u32 w, u32 h, u32 x, u32 y);
// ------------------------------------------------------------------------
virtual std::shared_ptr<std::atomic<VkImageView> > getImageView(
bool srgb = false) const
{
if (!m_ondemand_load)
{
m_image_view_lock.lock();
m_image_view_lock.unlock();
if (srgb && m_image_view_srgb)
return m_image_view_srgb;
else
return m_image_view;
}
else
return getImageViewLive(srgb);
}
// ------------------------------------------------------------------------
virtual bool useOnDemandLoad() const { return m_ondemand_load; }
// ------------------------------------------------------------------------
virtual const io::path& getFullPath() const { return m_full_path; }
// ------------------------------------------------------------------------
VkFormat getInternalFormat() const { return m_internal_format; }
// ------------------------------------------------------------------------
VkImage getImage() const
{
waitImageView();
return m_image;
}
// ------------------------------------------------------------------------
unsigned getMipmapLevels() const
{
if (!m_has_mipmaps)
return 1;
return std::floor(std::log2(std::max(m_size.Width, m_size.Height))) + 1;
}
}; // GEVulkanTexture
}
#endif
@@ -0,0 +1,240 @@
#include "ge_vulkan_texture_descriptor.hpp"
#include "ge_main.hpp"
#include "ge_material_manager.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_texture.hpp"
#include <algorithm>
#include <exception>
#include <stdexcept>
namespace GE
{
// ----------------------------------------------------------------------------
GEVulkanTextureDescriptor::GEVulkanTextureDescriptor(unsigned max_texture_list,
unsigned max_layer,
bool single_descriptor,
unsigned binding)
: m_max_texture_list(max_texture_list),
m_max_layer(max_layer), m_binding(binding)
{
if (m_max_layer > _IRR_MATERIAL_MAX_TEXTURES_)
{
throw std::runtime_error(
"Too large max_layer for GEVulkanTextureDescriptor");
}
m_vk = getVKDriver();
// m_descriptor_set_layout
std::vector<VkDescriptorSetLayoutBinding> texture_layout_binding;
texture_layout_binding.resize(1);
texture_layout_binding[0].binding = m_binding;
texture_layout_binding[0].descriptorCount =
single_descriptor ? m_max_texture_list * m_max_layer : 1;
texture_layout_binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
texture_layout_binding[0].pImmutableSamplers = NULL;
texture_layout_binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
if (!single_descriptor)
{
texture_layout_binding.resize(m_max_layer, texture_layout_binding[0]);
for (unsigned i = 1; i < m_max_layer; i++)
texture_layout_binding[i].binding = m_binding + i;
}
VkDescriptorSetLayoutCreateInfo setinfo = {};
setinfo.flags = 0;
setinfo.pNext = NULL;
setinfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
setinfo.pBindings = texture_layout_binding.data();
setinfo.bindingCount = texture_layout_binding.size();
if (vkCreateDescriptorSetLayout(m_vk->getDevice(), &setinfo,
NULL, &m_descriptor_set_layout) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorSetLayout failed for "
"GEVulkanTextureDescriptor");
}
// m_descriptor_pool
VkDescriptorPoolSize pool_size;
pool_size.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptorCount = m_max_texture_list * m_max_layer;
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = 0;
pool_info.maxSets = single_descriptor ? 1 : m_max_texture_list;
pool_info.poolSizeCount = 1;
pool_info.pPoolSizes = &pool_size;
if (vkCreateDescriptorPool(m_vk->getDevice(), &pool_info, NULL,
&m_descriptor_pool) != VK_SUCCESS)
{
throw std::runtime_error("vkCreateDescriptorPool failed for "
"GEVulkanTextureDescriptor");
}
// m_descriptor_sets
if (single_descriptor)
m_descriptor_sets.resize(1);
else
m_descriptor_sets.resize(m_max_texture_list);
std::vector<VkDescriptorSetLayout> layouts(m_descriptor_sets.size(),
m_descriptor_set_layout);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = layouts.size();
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(m_vk->getDevice(), &alloc_info,
m_descriptor_sets.data()) != VK_SUCCESS)
{
throw std::runtime_error("vkAllocateDescriptorSets failed for "
"GEVulkanTextureDescriptor");
}
m_sampler_use = GVS_NEAREST;
m_recreate_next_frame = false;
m_needs_update_descriptor = false;
GEVulkanTexture* tex = static_cast<GEVulkanTexture*>(
m_vk->getWhiteTexture());
m_white_image = tex->getImageView();
tex = static_cast<GEVulkanTexture*>(m_vk->getTransparentTexture());
m_transparent_image = tex->getImageView();
} // GEVulkanTextureDescriptor
// ----------------------------------------------------------------------------
GEVulkanTextureDescriptor::~GEVulkanTextureDescriptor()
{
vkDestroyDescriptorSetLayout(m_vk->getDevice(), m_descriptor_set_layout,
NULL);
vkDestroyDescriptorPool(m_vk->getDevice(), m_descriptor_pool, NULL);
} // ~GEVulkanTextureDescriptor
// ----------------------------------------------------------------------------
void GEVulkanTextureDescriptor::updateDescriptor()
{
if (!m_needs_update_descriptor)
return;
m_needs_update_descriptor = false;
if (m_texture_list.empty())
return;
std::vector<VkDescriptorImageInfo> image_infos;
image_infos.resize(m_texture_list.size() * m_max_layer);
for (auto& p : m_texture_list)
{
const size_t max_size = std::min((size_t)m_max_layer, p.first.size());
for (unsigned i = 0; i < max_size; i++)
{
VkDescriptorImageInfo info;
info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
info.sampler = m_vk->getSampler(m_sampler_use);
info.imageView = p.first[i].get()->load();
if (info.imageView == VK_NULL_HANDLE)
info.imageView = m_transparent_image.get()->load();
image_infos[p.second * m_max_layer + i] = info;
}
}
bool single_descriptor = (m_descriptor_sets.size() == 1);
if (single_descriptor)
{
VkDescriptorImageInfo dummy_info;
dummy_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
dummy_info.imageView = m_transparent_image.get()->load();
dummy_info.sampler = m_vk->getSampler(m_sampler_use);
image_infos.resize(m_max_texture_list * m_max_layer, dummy_info);
}
m_vk->waitIdle();
if (single_descriptor)
{
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = m_binding;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = m_max_texture_list * m_max_layer;
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_descriptor_sets[0];
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(m_vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
}
else
{
std::vector<VkWriteDescriptorSet> all_sets;
for (unsigned i = 0; i < image_infos.size(); i += m_max_layer)
{
const unsigned set_idx = i / m_max_layer;
VkWriteDescriptorSet write_descriptor_set = {};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstBinding = m_binding;
write_descriptor_set.dstArrayElement = 0;
write_descriptor_set.descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write_descriptor_set.descriptorCount = m_max_layer;
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = m_descriptor_sets[set_idx];
write_descriptor_set.pImageInfo = &image_infos[i];
all_sets.push_back(write_descriptor_set);
}
vkUpdateDescriptorSets(m_vk->getDevice(), all_sets.size(),
all_sets.data(), 0, NULL);
}
} // updateDescriptor
// ----------------------------------------------------------------------------
int GEVulkanTextureDescriptor::getTextureID(const irr::video::ITexture** list,
const std::string& shader)
{
TextureList key =
{{
m_white_image,
m_transparent_image,
m_transparent_image,
m_transparent_image,
m_transparent_image,
m_transparent_image,
m_transparent_image,
m_transparent_image
}};
const auto& material = GEMaterialManager::getMaterial(shader);
for (unsigned i = 0; i < m_max_layer; i++)
{
if (list[i])
{
key[i] = static_cast<const GEVulkanTexture*>(
list[i])->getImageView(getGEConfig()->m_pbr && material ?
material->m_srgb_settings[i] : false);
}
}
auto it = m_texture_list.find(key);
if (it != m_texture_list.end())
return it->second;
else
{
int cur_id = m_texture_list.size();
if (cur_id >= m_max_texture_list)
{
printf("Too many texture used in current frames\n");
m_recreate_next_frame = true;
return m_max_texture_list - 1;
}
m_texture_list[key] = cur_id;
m_needs_update_descriptor = true;
// Reset the list earlier if almost full
if (cur_id > int((float)m_max_texture_list * 0.8f))
m_recreate_next_frame = true;
return cur_id;
}
} // getTextureID
}
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
// From https://github.com/skeeto/w64devkit/blob/master/src/libchkstk.S
#if 0
# Implementations of ___chkstk_ms (GCC) and __chkstk (MSVC). Unlike
# libgcc, no work happens if the stack is already committed. Execute
# this source with a shell to build libchkstk.a.
# This is free and unencumbered software released into the public domain.
set -ex
${CC:-cc} -c -DCHKSTK_MS -Wa,--no-pad-sections -o chkstk_ms.o $0
${CC:-cc} -c -DCHKSTK -Wa,--no-pad-sections -o chkstk.o $0
rm -f "${DESTDIR}libchkstk.a"
${AR:-ar} r "${DESTDIR}libchkstk.a" chkstk_ms.o chkstk.o
rm chkstk_ms.o chkstk.o
exit 0
#endif
#if __amd64
// On x64, ___chkstk_ms and __chkstk have identical semantics. Unlike
// x86 __chkstk, neither adjusts the stack pointer. This implementation
// preserves all registers.
//
// The frame size is passed in rax, and this function ensures that
// enough of the stack is committed for the frame. It commits stack
// pages by writing to the guard page, one page at a time.
# if CHKSTK_MS
.globl ___chkstk_ms
___chkstk_ms:
# elif CHKSTK
.globl __chkstk
__chkstk:
# endif
push %rax
push %rcx
mov %gs:(0x10), %rcx // rcx = stack low address
neg %rax // rax = frame low address
add %rsp, %rax // "
jb 1f // frame low address overflow?
xor %eax, %eax // overflowed: frame low address = null
0: sub $0x1000, %rcx // extend stack into guard page
test %eax, (%rcx) // commit page (two instruction bytes)
1: cmp %rax, %rcx
ja 0b
pop %rcx
pop %rax
ret
#endif // __amd64
#if __i386
# if CHKSTK_MS
// Behaves exactly like x64 ___chkstk_ms.
.globl ___chkstk_ms
___chkstk_ms:
push %eax
push %ecx
mov %fs:(0x08), %ecx // ecx = stack low address
neg %eax // eax = frame low address
add %esp, %eax // "
jb 1f // frame low address overflow?
xor %eax, %eax // overflowed: frame low address = null
0: sub $0x1000, %ecx // extend stack into guard page
test %eax, (%ecx) // commit page (two instruction bytes)
1: cmp %eax, %ecx
ja 0b
pop %ecx
pop %eax
ret
# elif CHKSTK
// On x86, __chkstk allocates the new stack frame. This implementation
// clobbers eax. MSVC only seems to care about ebp and ecx (this).
.globl __chkstk
__chkstk:
push %ecx // preserve ecx
mov %fs:(0x08), %ecx // ecx = stack low address
neg %eax // eax = frame low address
lea 8(%esp,%eax), %eax // "
cmp %esp, %eax // frame low address overflow?
jb 1f // "
xor %eax, %eax // overflowed: frame low address = null
0: sub $0x1000, %ecx // extend stack into guard page
test %eax, (%ecx) // commit page (two instruction bytes)
1: cmp %eax, %ecx
ja 0b
pop %ecx // restore ecx
xchg %eax, %esp // allocate frame
jmp *(%eax) // return
# endif
#endif // __i386
File diff suppressed because it is too large Load Diff