SuperTuxKart 1.5 upstream source (from official release tarball)
This commit is contained in:
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
©_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, ®ion);
|
||||
} // 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, ®ion);
|
||||
|
||||
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
@@ -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
Reference in New Issue
Block a user