Add initial 2D vulkan rendering

This commit is contained in:
Benau 2022-03-19 15:14:55 +08:00
parent cb607a16ff
commit 1cf2c0c5bf
12 changed files with 1574 additions and 19 deletions

View File

@ -8,5 +8,5 @@ layout(location = 0) out vec4 o_color;
void main()
{
o_color = texture(f_tex[f_sampler_index], f_uv) * f_color;
o_color = texture(f_tex[GE_SAMPLE_TEX_INDEX(f_sampler_index)], f_uv) * f_color;
}

View File

@ -25,7 +25,10 @@ set(GE_SOURCES
src/ge_main.cpp
src/ge_texture.cpp
src/ge_dx9_texture.cpp
src/ge_vulkan_2d_renderer.cpp
src/ge_vulkan_driver.cpp
src/ge_vulkan_dynamic_buffer.cpp
src/ge_vulkan_features.cpp
src/ge_vulkan_shader_manager.cpp
src/ge_vulkan_texture.cpp
src/ge_gl_texture.cpp

View File

@ -11,7 +11,7 @@
#include "../source/Irrlicht/CNullDriver.h"
#include "SIrrCreationParameters.h"
#include "SColor.h"
#include <map>
#include <array>
#include <memory>
#include <string>
#include <vector>
@ -23,8 +23,9 @@ namespace GE
{
enum GEVulkanSampler : unsigned
{
GVS_MIN,
GVS_NEAREST = GVS_MIN
GVS_MIN = 0,
GVS_NEAREST = GVS_MIN,
GVS_COUNT,
};
class GEVulkanDriver : public video::CNullDriver
{
@ -40,10 +41,10 @@ namespace GE
virtual bool beginScene(bool backBuffer=true, bool zBuffer=true,
SColor color=SColor(255,0,0,0),
const SExposedVideoData& videoData=SExposedVideoData(),
core::rect<s32>* sourceRect=0) { return true; }
core::rect<s32>* sourceRect=0);
//! applications must call this method after performing any rendering. returns false if failed.
virtual bool endScene() { return true; }
virtual bool endScene();
//! queries the features of the driver, returns true if feature is available
virtual bool queryFeature(E_VIDEO_DRIVER_FEATURE feature) const { return true; }
@ -52,7 +53,7 @@ namespace GE
virtual void setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat) {}
//! sets a material
virtual void setMaterial(const SMaterial& material) {}
virtual void setMaterial(const SMaterial& material) { Material = material; }
//! sets a render target
virtual bool setRenderTarget(video::ITexture* texture,
@ -120,17 +121,17 @@ namespace GE
virtual void draw2DVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType,
E_INDEX_TYPE iType) {}
E_INDEX_TYPE iType);
//! draws an 2d image, using a color (if color is other then Color(255,255,255,255)) and the alpha channel of the texture if wanted.
virtual void draw2DImage(const video::ITexture* texture, const core::position2d<s32>& destPos,
const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect = 0,
SColor color=SColor(255,255,255,255), bool useAlphaChannelOfTexture=false) {}
SColor color=SColor(255,255,255,255), bool useAlphaChannelOfTexture=false);
//! Draws a part of the texture into the rectangle.
virtual void draw2DImage(const video::ITexture* texture, const core::rect<s32>& destRect,
const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect = 0,
const video::SColor* const colors=0, bool useAlphaChannelOfTexture=false) {}
const video::SColor* const colors=0, bool useAlphaChannelOfTexture=false);
//! Draws a set of 2d images, using a color and the alpha channel of the texture.
virtual void draw2DImageBatch(const video::ITexture* texture,
@ -138,7 +139,7 @@ namespace GE
const core::array<core::rect<s32> >& sourceRects,
const core::rect<s32>* clipRect=0,
SColor color=SColor(255,255,255,255),
bool useAlphaChannelOfTexture=false) {}
bool useAlphaChannelOfTexture=false);
//!Draws an 2d rectangle with a gradient.
virtual void draw2DRectangle(const core::rect<s32>& pos,
@ -268,11 +269,12 @@ namespace GE
virtual void enableScissorTest(const core::rect<s32>& r) {}
virtual void disableScissorTest() {}
virtual const core::dimension2d<u32>& getCurrentRenderTargetSize() const { return ScreenSize; }
VkSampler getSampler(GEVulkanSampler s) const
{
if (m_vk->samplers.find(s) == m_vk->samplers.end())
if (s >= GVS_COUNT)
return VK_NULL_HANDLE;
return m_vk->samplers.at(s);
return m_vk->samplers[s];
}
VkDevice getDevice() const { return m_vk->device; }
void destroyVulkan();
@ -286,7 +288,22 @@ namespace GE
VkCommandBuffer beginSingleTimeCommands();
void endSingleTimeCommands(VkCommandBuffer command_buffer);
io::IFileSystem* getFileSystem() const { return FileSystem; }
VkExtent2D getSwapChainExtent() const { return m_swap_chain_extent; }
size_t getSwapChainImagesCount() const
{ return m_vk->swap_chain_images.size(); }
VkRenderPass getRenderPass() const { return m_vk->render_pass; }
void copyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, VkDeviceSize size);
VkCommandBuffer getCurrentCommandBuffer()
{ return m_vk->command_buffers[m_current_frame]; }
std::vector<VkImage>& getSwapChainImages()
{ return m_vk->swap_chain_images; }
std::vector<VkFramebuffer>& getSwapChainFramebuffers()
{ return m_vk->swap_chain_framebuffers; }
unsigned int getCurrentFrame() const { return m_current_frame; }
unsigned int getCurrentImageIndex() const { return m_image_index; }
constexpr static unsigned getMaxFrameInFlight() { return 2; }
video::SColor getClearColor() const { return m_clear_color; }
private:
struct SwapChainSupportDetails
{
@ -320,6 +337,7 @@ namespace GE
E_GPU_SHADING_LANGUAGE shadingLang = EGSL_DEFAULT) { return 0; }
SIrrlichtCreationParameters m_params;
SMaterial Material;
// RAII to auto cleanup
struct VK
{
@ -334,7 +352,9 @@ namespace GE
std::vector<VkFence> in_flight_fences;
VkCommandPool command_pool;
std::vector<VkCommandBuffer> command_buffers;
std::map<GEVulkanSampler, VkSampler> samplers;
std::array<VkSampler, GVS_COUNT> samplers;
VkRenderPass render_pass;
std::vector<VkFramebuffer> swap_chain_framebuffers;
VK()
{
instance = VK_NULL_HANDLE;
@ -342,11 +362,16 @@ namespace GE
device = VK_NULL_HANDLE;
swap_chain = VK_NULL_HANDLE;
command_pool = VK_NULL_HANDLE;
samplers = {{}};
render_pass = VK_NULL_HANDLE;
}
~VK()
{
for (auto& sampler : samplers)
vkDestroySampler(device, sampler.second, NULL);
for (VkFramebuffer& framebuffer : swap_chain_framebuffers)
vkDestroyFramebuffer(device, framebuffer, NULL);
vkDestroyRenderPass(device, render_pass, NULL);
for (unsigned i = 0; i < GVS_COUNT; i++)
vkDestroySampler(device, samplers[i], NULL);
if (!command_buffers.empty())
{
vkFreeCommandBuffers(device, command_pool,
@ -389,6 +414,10 @@ namespace GE
VkPhysicalDeviceProperties m_properties;
VkPhysicalDeviceFeatures m_features;
unsigned int m_current_frame;
uint32_t m_image_index;
video::SColor m_clear_color;
void createInstance(SDL_Window* window);
void findPhysicalDevice();
bool checkDeviceExtensions(VkPhysicalDevice device);
@ -403,6 +432,8 @@ namespace GE
void createCommandPool();
void createCommandBuffers();
void createSamplers();
void createRenderPass();
void createFramebuffers();
std::string getVulkanVersionString() const;
std::string getDriverVersionString() const;
};

View File

@ -0,0 +1,478 @@
#include "ge_vulkan_2d_renderer.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_dynamic_buffer.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include <algorithm>
#include <cstddef>
#include <map>
#include <string>
#include <stdexcept>
#include <utility>
#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;
VkDescriptorPool g_descriptor_pool = VK_NULL_HANDLE;
VkDescriptorSetLayout g_descriptor_set_layout = VK_NULL_HANDLE;
GEVulkanDynamicBuffer* g_tris_buffer = NULL;
GEVulkanDynamicBuffer* g_tris_index_buffer = NULL;
std::vector<VkDescriptorSet> g_descriptor_sets;
struct Tri
{
core::vector2df pos;
video::SColor color;
core::vector2df uv;
int sampler_idx;
};
std::map<const irr::video::ITexture*, unsigned> g_tex_map;
std::vector<Tri> g_tris_queue;
std::vector<uint16_t> g_tris_index_queue;
} // GEVulkan2dRenderer
// ============================================================================
void GEVulkan2dRenderer::init(GEVulkanDriver* vk)
{
g_vk = vk;
createDescriptorSetLayout();
createPipelineLayout();
createGraphicsPipeline();
createTrisBuffers();
createDescriptorPool();
createDescriptorSets();
} // init
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::destroy()
{
delete g_tris_buffer;
g_tris_buffer = NULL;
delete g_tris_index_buffer;
g_tris_index_buffer = NULL;
vkDestroyDescriptorSetLayout(g_vk->getDevice(), g_descriptor_set_layout,
NULL);
vkDestroyDescriptorPool(g_vk->getDevice(), g_descriptor_pool, NULL);
vkDestroyPipeline(g_vk->getDevice(), g_graphics_pipeline, NULL);
vkDestroyPipelineLayout(g_vk->getDevice(), g_pipeline_layout, NULL);
g_descriptor_sets.clear();
} // destroy
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createDescriptorSetLayout()
{
VkDescriptorSetLayoutBinding sampler_layout_binding = {};
sampler_layout_binding.binding = 0;
sampler_layout_binding.descriptorCount = GEVulkanShaderManager::getSamplerSize();
sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
sampler_layout_binding.pImmutableSamplers = NULL;
sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
std::array<VkDescriptorSetLayoutBinding, 1> bindings =
{
sampler_layout_binding
};
VkDescriptorSetLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layout_info.bindingCount = (uint32_t)(bindings.size());
layout_info.pBindings = &bindings[0];
VkResult result = vkCreateDescriptorSetLayout(g_vk->getDevice(), &layout_info,
NULL, &g_descriptor_set_layout);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateDescriptorSetLayout failed");
} // createDescriptorSetLayout
// ----------------------------------------------------------------------------
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_descriptor_set_layout;
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::get2dRenderVert();
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::get2dRenderFrag();
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;
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.layout = g_pipeline_layout;
pipeline_info.renderPass = g_vk->getRenderPass();
pipeline_info.subpass = 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(GVDBT_SYSTEM_RAM,
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 10000);
g_tris_index_buffer = new GEVulkanDynamicBuffer(GVDBT_SYSTEM_RAM,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 2000);
} // createTrisBuffers
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createDescriptorPool()
{
uint32_t descriptor_count = g_vk->getMaxFrameInFlight();
std::array<VkDescriptorPoolSize, 1> pool_sizes = {};
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_sizes[0].descriptorCount = GEVulkanShaderManager::getSamplerSize() *
g_vk->getMaxFrameInFlight();
VkDescriptorPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.poolSizeCount = (uint32_t)(pool_sizes.size());
pool_info.pPoolSizes = &pool_sizes[0];
pool_info.maxSets = descriptor_count;
if (vkCreateDescriptorPool(g_vk->getDevice(), &pool_info, NULL,
&g_descriptor_pool) != VK_SUCCESS)
throw std::runtime_error("createDescriptorPool failed");
} // createDescriptorPool
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::createDescriptorSets()
{
g_descriptor_sets.resize(g_vk->getMaxFrameInFlight());
std::vector<VkDescriptorSetLayout> layouts(g_descriptor_sets.size(),
g_descriptor_set_layout);
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
alloc_info.descriptorPool = g_descriptor_pool;
alloc_info.descriptorSetCount = (uint32_t)(layouts.size());
alloc_info.pSetLayouts = layouts.data();
if (vkAllocateDescriptorSets(g_vk->getDevice(), &alloc_info,
g_descriptor_sets.data()) != VK_SUCCESS)
throw std::runtime_error("vkAllocateDescriptorSets failed");
} // createDescriptorSets
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::render()
{
std::array<VkClearValue, 2> clear_values = {};
video::SColorf cf(g_vk->getClearColor());
clear_values[0].color =
{
cf.getRed(), cf.getGreen(), cf.getBlue(), cf.getAlpha()
};
clear_values[1].depthStencil = {1.0f, 0};
VkRenderPassBeginInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_info.renderPass = g_vk->getRenderPass();
render_pass_info.framebuffer =
g_vk->getSwapChainFramebuffers()[g_vk->getCurrentImageIndex()];
render_pass_info.renderArea.offset = {0, 0};
render_pass_info.renderArea.extent = g_vk->getSwapChainExtent();
render_pass_info.clearValueCount = (uint32_t)(clear_values.size());
render_pass_info.pClearValues = &clear_values[0];
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (g_tex_map.empty())
{
VkResult result = vkBeginCommandBuffer(g_vk->getCurrentCommandBuffer(),
&begin_info);
if (result != VK_SUCCESS)
return;
vkCmdBeginRenderPass(g_vk->getCurrentCommandBuffer(), &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(g_vk->getCurrentCommandBuffer());
vkEndCommandBuffer(g_vk->getCurrentCommandBuffer());
return;
}
std::vector<std::pair<const irr::video::ITexture*, unsigned> > tex_map;
for (auto& tex : g_tex_map)
tex_map.emplace_back(tex.first, (unsigned)tex.second);
std::sort(tex_map.begin(), tex_map.end(),
[](const std::pair<const irr::video::ITexture*, unsigned>& a,
const std::pair<const irr::video::ITexture*, unsigned>& b)
{
return a.second < b.second;
});
std::vector<VkDescriptorImageInfo> image_infos;
for (auto& tex : tex_map)
{
VkDescriptorImageInfo image_info;
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
image_info.imageView = (VkImageView)tex.first->getTextureHandler();
image_info.sampler = g_vk->getSampler(GVS_NEAREST);
image_infos.push_back(image_info);
if (image_infos.size() >= GEVulkanShaderManager::getSamplerSize())
break;
}
image_infos.resize(GEVulkanShaderManager::getSamplerSize(), image_infos[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 = GEVulkanShaderManager::getSamplerSize();
write_descriptor_set.pBufferInfo = 0;
write_descriptor_set.dstSet = g_descriptor_sets[g_vk->getCurrentFrame()];
write_descriptor_set.pImageInfo = image_infos.data();
vkUpdateDescriptorSets(g_vk->getDevice(), 1, &write_descriptor_set, 0,
NULL);
VkDeviceSize offsets[] = {0};
VkBuffer vertex_buffer = VK_NULL_HANDLE;
VkBuffer indices_buffer = VK_NULL_HANDLE;
VkResult result = vkBeginCommandBuffer(g_vk->getCurrentCommandBuffer(),
&begin_info);
if (result != VK_SUCCESS)
goto end;
g_tris_buffer->setCurrentData(g_tris_queue.data(),
g_tris_queue.size() * sizeof(Tri));
g_tris_index_buffer->setCurrentData(g_tris_index_queue.data(),
g_tris_index_queue.size() * sizeof(uint16_t));
vkCmdBeginRenderPass(g_vk->getCurrentCommandBuffer(), &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
vertex_buffer = g_tris_buffer->getCurrentBuffer();
indices_buffer = g_tris_index_buffer->getCurrentBuffer();
if (vertex_buffer == VK_NULL_HANDLE || indices_buffer == VK_NULL_HANDLE)
goto end_cmd;
vkCmdBindPipeline(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_graphics_pipeline);
vkCmdBindVertexBuffers(g_vk->getCurrentCommandBuffer(), 0, 1,
&vertex_buffer, offsets);
vkCmdBindIndexBuffer(g_vk->getCurrentCommandBuffer(), indices_buffer, 0,
VK_INDEX_TYPE_UINT16);
vkCmdBindDescriptorSets(g_vk->getCurrentCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS, g_pipeline_layout, 0, 1,
&g_descriptor_sets[g_vk->getCurrentFrame()], 0, NULL);
vkCmdDrawIndexed(g_vk->getCurrentCommandBuffer(),
(uint32_t)(g_tris_index_queue.size()), 1, 0, 0, 0);
end_cmd:
vkCmdEndRenderPass(g_vk->getCurrentCommandBuffer());
vkEndCommandBuffer(g_vk->getCurrentCommandBuffer());
end:
g_tex_map.clear();
g_tris_queue.clear();
g_tris_index_queue.clear();
} // render
// ----------------------------------------------------------------------------
void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices,
unsigned vertices_count,
uint16_t* indices,
unsigned indices_count,
const irr::video::ITexture* t)
{
if (g_tex_map.find(t) == g_tex_map.end())
{
unsigned pre_size = g_tex_map.size();
g_tex_map[t] = pre_size;
}
int sampler_idx = g_tex_map.at(t);
// In STK we rarely use more than 96 textures per (2d) draw call
if (sampler_idx >= (int)GEVulkanShaderManager::getSamplerSize())
sampler_idx = GEVulkanShaderManager::getSamplerSize() - 1;
uint16_t last_index = (uint16_t)g_tris_queue.size();
if (last_index + vertices_count > 65535)
return;
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;
t.color = vertex.Color;
t.uv = vertex.TCoords;
t.sampler_idx = sampler_idx;
g_tris_queue.push_back(t);
}
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]);
}
} // addVerticesIndices
}

View File

@ -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 createDescriptorSetLayout();
// ----------------------------------------------------------------------------
void createDescriptorPool();
// ----------------------------------------------------------------------------
void createDescriptorSets();
// ----------------------------------------------------------------------------
void createPipelineLayout();
// ----------------------------------------------------------------------------
void createGraphicsPipeline();
// ----------------------------------------------------------------------------
void createTrisBuffers();
// ----------------------------------------------------------------------------
void render();
// ----------------------------------------------------------------------------
void addVerticesIndices(irr::video::S3DVertex* vertices,
unsigned vertices_count, uint16_t* indices,
unsigned indices_count,
const irr::video::ITexture* t);
}; // GEVulkanRenderer
}
#endif

View File

@ -1,9 +1,12 @@
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_2d_renderer.hpp"
#include "ge_vulkan_features.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_shader_manager.hpp"
#include "ge_vulkan_texture.hpp"
#ifdef _IRR_COMPILE_WITH_VULKAN_
const unsigned int MAX_FRAMES_IN_FLIGHT = 2;
#include "SDL_vulkan.h"
#include <algorithm>
#include <limits>
@ -453,6 +456,10 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
m_properties = {};
m_features = {};
m_current_frame = 0;
m_image_index = 0;
m_clear_color = video::SColor(0);
createInstance(window);
#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK)
@ -475,6 +482,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
m_device_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
findPhysicalDevice();
GEVulkanFeatures::init(this);
createDevice();
#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK)
@ -493,13 +501,19 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params,
createCommandPool();
createCommandBuffers();
createSamplers();
createRenderPass();
createFramebuffers();
GEVulkanShaderManager::init(this);
// For GEVulkanDynamicBuffer
GE::setVideoDriver(this);
GEVulkan2dRenderer::init(this);
os::Printer::log("Vulkan version", getVulkanVersionString().c_str());
os::Printer::log("Vulkan vendor", getVendorInfo().c_str());
os::Printer::log("Vulkan renderer", m_properties.deviceName);
os::Printer::log("Vulkan driver version", getDriverVersionString().c_str());
for (const char* ext : m_device_extensions)
os::Printer::log("Vulkan enabled extension", ext);
GEVulkanFeatures::printStats();
} // GEVulkanDriver
// ----------------------------------------------------------------------------
@ -510,6 +524,7 @@ GEVulkanDriver::~GEVulkanDriver()
// ----------------------------------------------------------------------------
void GEVulkanDriver::destroyVulkan()
{
GEVulkan2dRenderer::destroy();
GEVulkanShaderManager::destroy();
delete m_vk.get();
m_vk.release();
@ -518,6 +533,9 @@ void GEVulkanDriver::destroyVulkan()
// ----------------------------------------------------------------------------
void GEVulkanDriver::createInstance(SDL_Window* window)
{
#define VK_MAKE_API_VERSION(variant, major, minor, patch) \
((((uint32_t)(variant)) << 29) | (((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK)
if (gladLoadVulkanUserPtr(NULL, (GLADuserptrloadfunc)loader, NULL) == 0)
{
@ -531,10 +549,43 @@ void GEVulkanDriver::createInstance(SDL_Window* window)
std::vector<const char*> extensions(count, NULL);
if (!SDL_Vulkan_GetInstanceExtensions(window, &count, extensions.data()))
throw std::runtime_error("SDL_Vulkan_GetInstanceExtensions failed with extensions vector");
uint32_t vk_version = 0;
bool vulkan_1_1 = false;
#if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK)
PFN_vkEnumerateInstanceVersion e_ver = (PFN_vkEnumerateInstanceVersion)
vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceVersion");
vulkan_1_1 = (e_ver && e_ver(&vk_version) == VK_SUCCESS &&
vk_version >= VK_MAKE_API_VERSION(0, 1, 1, 0));
#else
vulkan_1_1 = (vkEnumerateInstanceVersion(&vk_version) == VK_SUCCESS &&
vk_version >= VK_MAKE_API_VERSION(0, 1, 1, 0));
#endif
uint32_t layer_count = 0;
vkEnumerateInstanceLayerProperties(&layer_count, NULL);
std::vector<VkLayerProperties> available_layers(layer_count);
vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data());
std::vector<const char*> enabled_validation_layers;
#ifdef ENABLE_VALIDATION
for (VkLayerProperties& prop : available_layers)
{
if (std::string(prop.layerName) == "VK_LAYER_KHRONOS_validation")
enabled_validation_layers.push_back("VK_LAYER_KHRONOS_validation");
}
#endif
VkInstanceCreateInfo create_info = {};
VkApplicationInfo app_info = {};
if (vulkan_1_1)
app_info.apiVersion = vk_version;
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.enabledExtensionCount = extensions.size();
create_info.ppEnabledExtensionNames = extensions.data();
create_info.pApplicationInfo = &app_info;
create_info.enabledLayerCount = enabled_validation_layers.size();
create_info.ppEnabledLayerNames = enabled_validation_layers.data();
VkResult result = vkCreateInstance(&create_info, NULL, &m_vk->instance);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateInstance failed");
@ -709,6 +760,15 @@ void GEVulkanDriver::createDevice()
throw std::runtime_error("doesn't support shaderSampledImageArrayDynamicIndexing");
device_features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
VkPhysicalDeviceVulkan12Features vulkan12_features = {};
vulkan12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
vulkan12_features.descriptorIndexing =
GEVulkanFeatures::supportsDescriptorIndexing();
vulkan12_features.shaderSampledImageArrayNonUniformIndexing =
GEVulkanFeatures::supportsNonUniformIndexing();
vulkan12_features.descriptorBindingPartiallyBound =
GEVulkanFeatures::supportsPartiallyBound();
if (m_features.samplerAnisotropy == VK_TRUE)
device_features.samplerAnisotropy = VK_TRUE;
@ -720,6 +780,7 @@ void GEVulkanDriver::createDevice()
create_info.enabledExtensionCount = m_device_extensions.size();
create_info.ppEnabledExtensionNames = &m_device_extensions[0];
create_info.enabledLayerCount = 0;
create_info.pNext = &vulkan12_features;
VkResult result = vkCreateDevice(m_physical_device, &create_info, NULL, &m_vk->device);
@ -898,6 +959,8 @@ found_mode:
m_swap_chain_image_format = surface_format.format;
m_swap_chain_extent = image_extent;
ScreenSize.Width = m_swap_chain_extent.width;
ScreenSize.Height = m_swap_chain_extent.height;
for (unsigned int i = 0; i < m_vk->swap_chain_images.size(); i++)
{
@ -936,7 +999,7 @@ void GEVulkanDriver::createSyncObjects()
fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_info.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (unsigned int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
for (unsigned int i = 0; i < getMaxFrameInFlight(); i++)
{
VkSemaphore image_available_semaphore;
VkResult result = vkCreateSemaphore(m_vk->device, &semaphore_info, NULL,
@ -977,6 +1040,7 @@ void GEVulkanDriver::createCommandPool()
VkCommandPoolCreateInfo pool_info = {};
pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
pool_info.queueFamilyIndex = m_graphics_family;
pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
VkResult result = vkCreateCommandPool(m_vk->device, &pool_info, NULL,
&m_vk->command_pool);
@ -988,7 +1052,7 @@ void GEVulkanDriver::createCommandPool()
// ----------------------------------------------------------------------------
void GEVulkanDriver::createCommandBuffers()
{
std::vector<VkCommandBuffer> buffers(MAX_FRAMES_IN_FLIGHT);
std::vector<VkCommandBuffer> buffers(getMaxFrameInFlight());
VkCommandBufferAllocateInfo alloc_info = {};
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
@ -1033,6 +1097,84 @@ void GEVulkanDriver::createSamplers()
m_vk->samplers[GVS_NEAREST] = sampler;
} // createSamplers
// ----------------------------------------------------------------------------
void GEVulkanDriver::createRenderPass()
{
VkAttachmentDescription color_attachment = {};
color_attachment.format = m_swap_chain_image_format;
color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference color_attachment_ref = {};
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
VkSubpassDependency dependency = {};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
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_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
std::array<VkAttachmentDescription, 1> attachments = { color_attachment };
VkRenderPassCreateInfo render_pass_info = {};
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount = (uint32_t)(attachments.size());
render_pass_info.pAttachments = &attachments[0];
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass;
render_pass_info.dependencyCount = 1;
render_pass_info.pDependencies = &dependency;
VkResult result = vkCreateRenderPass(m_vk->device, &render_pass_info, NULL,
&m_vk->render_pass);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateRenderPass failed");
} // createRenderPass
// ----------------------------------------------------------------------------
void GEVulkanDriver::createFramebuffers()
{
const std::vector<VkImageView>& image_views = m_vk->swap_chain_image_views;
for (unsigned int i = 0; i < image_views.size(); i++)
{
std::array<VkImageView, 1> attachments =
{
image_views[i]
};
VkFramebufferCreateInfo framebuffer_info = {};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = m_vk->render_pass;
framebuffer_info.attachmentCount = (uint32_t)(attachments.size());
framebuffer_info.pAttachments = &attachments[0];
framebuffer_info.width = m_swap_chain_extent.width;
framebuffer_info.height = m_swap_chain_extent.height;
framebuffer_info.layers = 1;
VkFramebuffer swap_chain_framebuffer = VK_NULL_HANDLE;
VkResult result = vkCreateFramebuffer(m_vk->device, &framebuffer_info,
NULL, &swap_chain_framebuffer);
if (result != VK_SUCCESS)
throw std::runtime_error("vkCreateFramebuffer failed");
m_vk->swap_chain_framebuffers.push_back(swap_chain_framebuffer);
}
} // createFramebuffers
// ----------------------------------------------------------------------------
bool GEVulkanDriver::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
VkMemoryPropertyFlags properties,
@ -1087,6 +1229,19 @@ bool GEVulkanDriver::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
return true;
} // createBuffer
// ----------------------------------------------------------------------------
void GEVulkanDriver::copyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer,
VkDeviceSize size)
{
VkCommandBuffer command_buffer = beginSingleTimeCommands();
VkBufferCopy copy_region = {};
copy_region.size = size;
vkCmdCopyBuffer(command_buffer, src_buffer, dst_buffer, 1, &copy_region);
endSingleTimeCommands(command_buffer);
} // copyBuffer
// ----------------------------------------------------------------------------
VkCommandBuffer GEVulkanDriver::beginSingleTimeCommands()
{
@ -1130,6 +1285,418 @@ void GEVulkanDriver::OnResize(const core::dimension2d<u32>& size)
CNullDriver::OnResize(size);
} // OnResize
// ----------------------------------------------------------------------------
bool GEVulkanDriver::beginScene(bool backBuffer, bool zBuffer, SColor color,
const SExposedVideoData& videoData,
core::rect<s32>* sourceRect)
{
if (!video::CNullDriver::beginScene(backBuffer, zBuffer, color, videoData,
sourceRect))
return false;
m_clear_color = color;
VkFence fence = m_vk->in_flight_fences[m_current_frame];
vkWaitForFences(m_vk->device, 1, &fence, VK_TRUE,
std::numeric_limits<uint64_t>::max());
vkResetFences(m_vk->device, 1, &fence);
VkSemaphore semaphore = m_vk->image_available_semaphores[m_current_frame];
VkResult result = vkAcquireNextImageKHR(m_vk->device, m_vk->swap_chain,
std::numeric_limits<uint64_t>::max(), semaphore, VK_NULL_HANDLE,
&m_image_index);
return (result != VK_ERROR_OUT_OF_DATE_KHR);
} // beginScene
// ----------------------------------------------------------------------------
bool GEVulkanDriver::endScene()
{
GEVulkan2dRenderer::render();
VkSemaphore wait_semaphores[] = {m_vk->image_available_semaphores[m_current_frame]};
VkPipelineStageFlags wait_stages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSemaphore signal_semaphores[] = {m_vk->render_finished_semaphores[m_current_frame]};
VkSubmitInfo submit_info = {};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = wait_semaphores;
submit_info.pWaitDstStageMask = wait_stages;
submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &m_vk->command_buffers[m_current_frame];
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = signal_semaphores;
VkResult result = vkQueueSubmit(m_graphics_queue, 1, &submit_info,
m_vk->in_flight_fences[m_current_frame]);
if (result != VK_SUCCESS)
return false;
VkSemaphore semaphores[] =
{
m_vk->render_finished_semaphores[m_current_frame]
};
VkSwapchainKHR swap_chains[] =
{
m_vk->swap_chain
};
VkPresentInfoKHR present_info = {};
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
present_info.waitSemaphoreCount = 1;
present_info.pWaitSemaphores = semaphores;
present_info.swapchainCount = 1;
present_info.pSwapchains = swap_chains;
present_info.pImageIndices = &m_image_index;
m_current_frame = (m_current_frame + 1) % getMaxFrameInFlight();
result = vkQueuePresentKHR(m_present_queue, &present_info);
if (!video::CNullDriver::endScene())
return false;
return (result != VK_ERROR_OUT_OF_DATE_KHR && result != VK_SUBOPTIMAL_KHR);
} // endScene
// ----------------------------------------------------------------------------
void GEVulkanDriver::draw2DVertexPrimitiveList(const void* vertices,
u32 vertexCount,
const void* indexList,
u32 primitiveCount,
E_VERTEX_TYPE vType,
scene::E_PRIMITIVE_TYPE pType,
E_INDEX_TYPE iType)
{
const GEVulkanTexture* texture =
dynamic_cast<const GEVulkanTexture*>(Material.getTexture(0));
if (!texture)
return;
if (vType != EVT_STANDARD || iType != EIT_16BIT)
return;
if (pType == irr::scene::EPT_TRIANGLES)
{
S3DVertex* v = (S3DVertex*)vertices;
u16* i = (u16*)indexList;
GEVulkan2dRenderer::addVerticesIndices(v, vertexCount, i,
primitiveCount, texture);
}
} // draw2DVertexPrimitiveList
// ----------------------------------------------------------------------------
void GEVulkanDriver::draw2DImage(const video::ITexture* tex,
const core::position2d<s32>& destPos,
const core::rect<s32>& sourceRect,
const core::rect<s32>* clipRect,
SColor color, bool useAlphaChannelOfTexture)
{
const GEVulkanTexture* texture = dynamic_cast<const GEVulkanTexture*>(tex);
if (!texture)
return;
if (!sourceRect.isValid())
return;
core::position2d<s32> targetPos = destPos;
core::position2d<s32> sourcePos = sourceRect.UpperLeftCorner;
// This needs to be signed as it may go negative.
core::dimension2d<s32> sourceSize(sourceRect.getSize());
if (clipRect)
{
if (targetPos.X < clipRect->UpperLeftCorner.X)
{
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
if (sourceSize.Width <= 0)
return;
sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X;
targetPos.X = clipRect->UpperLeftCorner.X;
}
if (targetPos.X + (s32)sourceSize.Width > clipRect->LowerRightCorner.X)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X;
if (sourceSize.Width <= 0)
return;
}
if (targetPos.Y < clipRect->UpperLeftCorner.Y)
{
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
if (sourceSize.Height <= 0)
return;
sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y;
targetPos.Y = clipRect->UpperLeftCorner.Y;
}
if (targetPos.Y + (s32)sourceSize.Height > clipRect->LowerRightCorner.Y)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y;
if (sourceSize.Height <= 0)
return;
}
}
// clip these coordinates
if (targetPos.X<0)
{
sourceSize.Width += targetPos.X;
if (sourceSize.Width <= 0)
return;
sourcePos.X -= targetPos.X;
targetPos.X = 0;
}
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width;
if (sourceSize.Width <= 0)
return;
}
if (targetPos.Y<0)
{
sourceSize.Height += targetPos.Y;
if (sourceSize.Height <= 0)
return;
sourcePos.Y -= targetPos.Y;
targetPos.Y = 0;
}
if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height;
if (sourceSize.Height <= 0)
return;
}
// ok, we've clipped everything.
// now draw it.
core::rect<f32> tcoords;
tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getSize().Width ;
tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getSize().Height;
tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getSize().Width);
tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getSize().Height);
const core::rect<s32> poss(targetPos, sourceSize);
S3DVertex vtx[4];
vtx[0] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
vtx[1] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
vtx[2] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
vtx[3] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
u16 indices[6] = {0,1,2,0,2,3};
GEVulkan2dRenderer::addVerticesIndices(&vtx[0], 4, &indices[0], 2, texture);
} // draw2DImage
// ----------------------------------------------------------------------------
void GEVulkanDriver::draw2DImage(const video::ITexture* tex,
const core::rect<s32>& destRect,
const core::rect<s32>& sourceRect,
const core::rect<s32>* clipRect,
const video::SColor* const colors,
bool useAlphaChannelOfTexture)
{
const GEVulkanTexture* texture = dynamic_cast<const GEVulkanTexture*>(tex);
if (!texture)
return;
const core::dimension2d<u32>& ss = texture->getSize();
core::rect<f32> tcoords;
tcoords.UpperLeftCorner.X = (f32)sourceRect.UpperLeftCorner.X / (f32)ss.Width;
tcoords.UpperLeftCorner.Y = (f32)sourceRect.UpperLeftCorner.Y / (f32)ss.Height;
tcoords.LowerRightCorner.X = (f32)sourceRect.LowerRightCorner.X / (f32)ss.Width;
tcoords.LowerRightCorner.Y = (f32)sourceRect.LowerRightCorner.Y / (f32)ss.Height;
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
const video::SColor temp[4] =
{
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF
};
const video::SColor* const useColor = colors ? colors : temp;
S3DVertex vtx[4];
vtx[0] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, useColor[0],
tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
vtx[1] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, useColor[3],
tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
vtx[2] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, useColor[2],
tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
vtx[3] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, useColor[1],
tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
u16 indices[6] = {0,1,2,0,2,3};
GEVulkan2dRenderer::addVerticesIndices(&vtx[0], 4, &indices[0], 2, texture);
} // draw2DImage
// ----------------------------------------------------------------------------
void GEVulkanDriver::draw2DImageBatch(const video::ITexture* tex,
const core::array<core::position2d<s32> >& positions,
const core::array<core::rect<s32> >& sourceRects,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture)
{
const GEVulkanTexture* texture = dynamic_cast<const GEVulkanTexture*>(tex);
if (!texture)
return;
const irr::u32 drawCount = core::min_<u32>(positions.size(), sourceRects.size());
core::array<S3DVertex> vtx(drawCount * 4);
core::array<u16> indices(drawCount * 6);
for(u32 i = 0;i < drawCount;i++)
{
core::position2d<s32> targetPos = positions[i];
core::position2d<s32> sourcePos = sourceRects[i].UpperLeftCorner;
// This needs to be signed as it may go negative.
core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
if (clipRect)
{
if (targetPos.X < clipRect->UpperLeftCorner.X)
{
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
if (sourceSize.Width <= 0)
continue;
sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X;
targetPos.X = clipRect->UpperLeftCorner.X;
}
if (targetPos.X + (s32)sourceSize.Width > clipRect->LowerRightCorner.X)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X;
if (sourceSize.Width <= 0)
continue;
}
if (targetPos.Y < clipRect->UpperLeftCorner.Y)
{
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
if (sourceSize.Height <= 0)
continue;
sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y;
targetPos.Y = clipRect->UpperLeftCorner.Y;
}
if (targetPos.Y + (s32)sourceSize.Height > clipRect->LowerRightCorner.Y)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y;
if (sourceSize.Height <= 0)
continue;
}
}
// clip these coordinates
if (targetPos.X<0)
{
sourceSize.Width += targetPos.X;
if (sourceSize.Width <= 0)
continue;
sourcePos.X -= targetPos.X;
targetPos.X = 0;
}
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width;
if (sourceSize.Width <= 0)
continue;
}
if (targetPos.Y<0)
{
sourceSize.Height += targetPos.Y;
if (sourceSize.Height <= 0)
continue;
sourcePos.Y -= targetPos.Y;
targetPos.Y = 0;
}
if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height;
if (sourceSize.Height <= 0)
continue;
}
// ok, we've clipped everything.
// now draw it.
core::rect<f32> tcoords;
tcoords.UpperLeftCorner.X = (((f32)sourcePos.X)) / texture->getSize().Width ;
tcoords.UpperLeftCorner.Y = (((f32)sourcePos.Y)) / texture->getSize().Height;
tcoords.LowerRightCorner.X = tcoords.UpperLeftCorner.X + ((f32)(sourceSize.Width) / texture->getSize().Width);
tcoords.LowerRightCorner.Y = tcoords.UpperLeftCorner.Y + ((f32)(sourceSize.Height) / texture->getSize().Height);
const core::rect<s32> poss(targetPos, sourceSize);
vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y));
vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y));
vtx.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y));
vtx.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0.0f,
0.0f, 0.0f, 0.0f, color,
tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y));
const u32 curPos = vtx.size()-4;
indices.push_back(0+curPos);
indices.push_back(1+curPos);
indices.push_back(2+curPos);
indices.push_back(0+curPos);
indices.push_back(2+curPos);
indices.push_back(3+curPos);
}
if (vtx.size())
{
GEVulkan2dRenderer::addVerticesIndices(vtx.pointer(), vtx.size(),
indices.pointer(), indices.size() / 3, texture);
}
} // draw2DImageBatch
}
namespace irr

View File

@ -0,0 +1,227 @@
#include "ge_vulkan_dynamic_buffer.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_main.hpp"
#include <array>
#include <vector>
#include <functional>
namespace GE
{
VkMemoryPropertyFlags GEVulkanDynamicBuffer::m_host_flag = (VkMemoryPropertyFlags)-1;
// ----------------------------------------------------------------------------
GEVulkanDynamicBuffer::GEVulkanDynamicBuffer(GEVulkanDynamicBufferType t,
VkBufferUsageFlags usage,
size_t initial_size)
{
m_type = t;
m_usage = usage;
m_size = initial_size;
m_buffer = new VkBuffer[GEVulkanDriver::getMaxFrameInFlight()];
m_memory = new VkDeviceMemory[GEVulkanDriver::getMaxFrameInFlight()];
m_mapped_addr = new void*[GEVulkanDriver::getMaxFrameInFlight()];
if (t == GVDBT_GPU_RAM)
{
m_staging_buffer = new VkBuffer[GEVulkanDriver::getMaxFrameInFlight()];
m_staging_memory = new VkDeviceMemory[GEVulkanDriver::getMaxFrameInFlight()];
}
else
{
m_staging_buffer = NULL;
m_staging_memory = NULL;
}
for (unsigned i = 0; i < GEVulkanDriver::getMaxFrameInFlight(); i++)
initPerFrame(i);
} // GEVulkanDynamicBuffer
// ----------------------------------------------------------------------------
GEVulkanDynamicBuffer::~GEVulkanDynamicBuffer()
{
destroy();
delete [] m_buffer;
delete [] m_memory;
delete [] m_staging_buffer;
delete [] m_staging_memory;
delete [] m_mapped_addr;
} // ~GEVulkanDynamicBuffer
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::initPerFrame(unsigned frame)
{
m_buffer[frame] = VK_NULL_HANDLE;
m_memory[frame] = VK_NULL_HANDLE;
m_mapped_addr[frame] = NULL;
if (m_type == GVDBT_GPU_RAM)
{
m_staging_buffer[frame] = VK_NULL_HANDLE;
m_staging_memory[frame] = VK_NULL_HANDLE;
}
VkBuffer host_buffer = VK_NULL_HANDLE;
VkDeviceMemory host_memory = VK_NULL_HANDLE;
VkMemoryPropertyFlags host_flag = 0;
if (m_host_flag == (VkFlags)-1)
{
// https://zeux.io/2020/02/27/writing-an-efficient-vulkan-renderer/
m_host_flag = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
bool better_flag_exist = false;
if (getVKDriver()->createBuffer(m_size,
m_usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, m_host_flag,
host_buffer, host_memory))
{
better_flag_exist = true;
if (m_type == GVDBT_SYSTEM_RAM)
goto succeed;
}
if (host_buffer != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), host_buffer, NULL);
host_buffer = VK_NULL_HANDLE;
if (host_memory != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), host_memory, NULL);
host_memory = VK_NULL_HANDLE;
if (!better_flag_exist)
{
m_host_flag = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
}
host_flag = m_host_flag;
// From the above website:
// This flag should be used to store staging buffers that are used to
// populate static resources allocated with
// VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with data.
if (m_type == GVDBT_GPU_RAM)
{
host_flag = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
if (!getVKDriver()->createBuffer(m_size,
m_usage | VK_BUFFER_USAGE_TRANSFER_SRC_BIT, host_flag,
host_buffer, host_memory))
{
if (host_buffer != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), host_buffer, NULL);
if (host_memory != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), host_memory, NULL);
return;
}
succeed:
if (m_type == GVDBT_SYSTEM_RAM)
{
m_buffer[frame] = host_buffer;
m_memory[frame] = host_memory;
if (vkMapMemory(getVKDriver()->getDevice(), m_memory[frame], 0, m_size,
0, &m_mapped_addr[frame]) != VK_SUCCESS)
{
destroyPerFrame(frame);
}
return;
}
VkBuffer local_buffer = VK_NULL_HANDLE;
VkDeviceMemory local_memory = VK_NULL_HANDLE;
if (!getVKDriver()->createBuffer(m_size,
m_usage | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, local_buffer, local_memory))
{
if (host_buffer != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), host_buffer, NULL);
if (host_memory != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), host_memory, NULL);
if (local_buffer != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), local_buffer, NULL);
if (local_memory != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), local_memory, NULL);
return;
}
m_buffer[frame] = local_buffer;
m_memory[frame] = local_memory;
m_staging_buffer[frame] = host_buffer;
m_staging_memory[frame] = host_memory;
if (vkMapMemory(getVKDriver()->getDevice(), m_staging_memory[frame], 0,
m_size, 0, &m_mapped_addr[frame]) != VK_SUCCESS)
{
destroyPerFrame(frame);
}
} // initPerFrame
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::destroyPerFrame(unsigned frame)
{
if ((m_staging_memory && m_staging_memory[frame] != VK_NULL_HANDLE) ||
m_memory[frame] != VK_NULL_HANDLE)
{
vkUnmapMemory(getVKDriver()->getDevice(), m_type == GVDBT_GPU_RAM ?
m_staging_memory[frame] : m_memory[frame]);
}
if (m_memory[frame] != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), m_memory[frame], NULL);
if (m_buffer[frame] != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), m_buffer[frame], NULL);
m_buffer[frame] = VK_NULL_HANDLE;
m_memory[frame] = VK_NULL_HANDLE;
m_mapped_addr[frame] = NULL;
if (m_type == GVDBT_GPU_RAM)
{
if (m_staging_buffer[frame] != VK_NULL_HANDLE)
vkDestroyBuffer(getVKDriver()->getDevice(), m_staging_buffer[frame], NULL);
if (m_staging_memory[frame] != VK_NULL_HANDLE)
vkFreeMemory(getVKDriver()->getDevice(), m_staging_memory[frame], NULL);
m_staging_buffer[frame] = VK_NULL_HANDLE;
m_staging_memory[frame] = VK_NULL_HANDLE;
}
} // destroyPerFrame
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::destroy()
{
vkDeviceWaitIdle(getVKDriver()->getDevice());
for (unsigned i = 0; i < GEVulkanDriver::getMaxFrameInFlight(); i++)
destroyPerFrame(i);
} // destroy
// ----------------------------------------------------------------------------
void GEVulkanDynamicBuffer::setCurrentData(void* data, size_t size)
{
const unsigned cur_frame = getVKDriver()->getCurrentFrame();
if (size > m_size)
{
destroy();
m_size = size + 100;
for (unsigned i = 0; i < GEVulkanDriver::getMaxFrameInFlight(); i++)
initPerFrame(i);
}
if (m_mapped_addr[cur_frame] == NULL)
return;
memcpy(m_mapped_addr[cur_frame], data, size);
if (m_type == GVDBT_GPU_RAM)
{
VkBufferCopy copy_region = {};
copy_region.size = size;
vkCmdCopyBuffer(getVKDriver()->getCurrentCommandBuffer(),
m_staging_buffer[cur_frame], m_buffer[cur_frame], 1, &copy_region);
}
} // setCurrentData
// ----------------------------------------------------------------------------
VkBuffer GEVulkanDynamicBuffer::getCurrentBuffer() const
{
return m_buffer[getVKDriver()->getCurrentFrame()];
} // getCurrentBuffer
}

View File

@ -0,0 +1,56 @@
#ifndef HEADER_GE_VULKAN_DYNAMIC_BUFFER_HPP
#define HEADER_GE_VULKAN_DYNAMIC_BUFFER_HPP
#include "vulkan_wrapper.h"
namespace GE
{
enum GEVulkanDynamicBufferType : unsigned
{
GVDBT_GPU_RAM,
GVDBT_SYSTEM_RAM
};
class GEVulkanDynamicBuffer
{
private:
VkBuffer* m_buffer;
VkDeviceMemory* m_memory;
VkBuffer* m_staging_buffer;
VkDeviceMemory* m_staging_memory;
void** m_mapped_addr;
VkBufferUsageFlags m_usage;
GEVulkanDynamicBufferType m_type;
static VkMemoryPropertyFlags m_host_flag;
size_t m_size;
// ------------------------------------------------------------------------
void initPerFrame(unsigned frame);
// ------------------------------------------------------------------------
void destroyPerFrame(unsigned frame);
// ------------------------------------------------------------------------
void destroy();
public:
// ------------------------------------------------------------------------
GEVulkanDynamicBuffer(GEVulkanDynamicBufferType t,
VkBufferUsageFlags usage, size_t initial_size);
// ------------------------------------------------------------------------
~GEVulkanDynamicBuffer();
// ------------------------------------------------------------------------
void setCurrentData(void* data, size_t size);
// ------------------------------------------------------------------------
VkBuffer getCurrentBuffer() const;
}; // GEVulkanDynamicBuffer
}
#endif

View File

@ -0,0 +1,90 @@
#include "ge_vulkan_features.hpp"
#include "ge_vulkan_driver.hpp"
#include <set>
#include <string>
#include <vector>
#include "../source/Irrlicht/os.h"
namespace GE
{
namespace GEVulkanFeatures
{
// ============================================================================
// 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;
} // GEVulkanFeatures
// ============================================================================
void GEVulkanFeatures::init(GEVulkanDriver* vk)
{
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 (std::string(prop.extensionName) == "VK_EXT_descriptor_indexing")
g_supports_descriptor_indexing = true;
}
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;
if (!vkGetPhysicalDeviceFeatures2)
return;
vkGetPhysicalDeviceFeatures2(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);
} // init
// ----------------------------------------------------------------------------
void GEVulkanFeatures::printStats()
{
os::Printer::log(
"Vulkan supports VK_EXT_descriptor_indexing",
g_supports_descriptor_indexing ? "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::supportsDescriptorIndexing()
{
return g_supports_descriptor_indexing;
} // supportsDescriptorIndexing
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsNonUniformIndexing()
{
return g_supports_non_uniform_indexing;
} // supportsNonUniformIndexing
// ----------------------------------------------------------------------------
bool GEVulkanFeatures::supportsPartiallyBound()
{
return g_supports_partially_bound;
} // supportsPartiallyBound
}

View File

@ -0,0 +1,25 @@
#ifndef HEADER_GE_VULKAN_FEATURES_HPP
#define HEADER_GE_VULKAN_FEATURES_HPP
#include "vulkan_wrapper.h"
namespace GE
{
class GEVulkanDriver;
namespace GEVulkanFeatures
{
// ----------------------------------------------------------------------------
void init(GEVulkanDriver*);
// ----------------------------------------------------------------------------
void printStats();
// ----------------------------------------------------------------------------
bool supportsDescriptorIndexing();
// ----------------------------------------------------------------------------
bool supportsNonUniformIndexing();
// ----------------------------------------------------------------------------
bool supportsPartiallyBound();
}; // GEVulkanFeatures
}
#endif

View File

@ -2,6 +2,7 @@
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
#include <sstream>
@ -12,6 +13,8 @@
namespace GE
{
namespace GEVulkanShaderManager
{
// ============================================================================
GEVulkanDriver* g_vk = NULL;
irr::io::IFileSystem* g_file_system = NULL;
@ -21,6 +24,8 @@ uint32_t g_sampler_size = 0;
VkShaderModule g_2d_render_vert = VK_NULL_HANDLE;
VkShaderModule g_2d_render_frag = VK_NULL_HANDLE;
} // GEVulkanShaderManager
// ============================================================================
void GEVulkanShaderManager::init(GEVulkanDriver* vk)
{
@ -37,6 +42,13 @@ void GEVulkanShaderManager::init(GEVulkanDriver* vk)
std::ostringstream oss;
oss << "#version 450\n";
oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n";
if (GEVulkanFeatures::supportsDescriptorIndexing())
{
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";
g_predefines = oss.str();
// 2D rendering shader
@ -99,4 +111,23 @@ VkShaderModule GEVulkanShaderManager::loadShader(shaderc_shader_kind kind,
return shader_module;
} // loadShader
// ----------------------------------------------------------------------------
unsigned GEVulkanShaderManager::getSamplerSize()
{
return g_sampler_size;
} // getSamplerSize
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::get2dRenderVert()
{
return g_2d_render_vert;
} // get2dRenderVert
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::get2dRenderFrag()
{
return g_2d_render_frag;
} // get2dRenderFrag
// ----------------------------------------------------------------------------
}

View File

@ -16,6 +16,12 @@ void init(GEVulkanDriver*);
void destroy();
// ----------------------------------------------------------------------------
VkShaderModule loadShader(shaderc_shader_kind, const std::string&);
// ----------------------------------------------------------------------------
VkShaderModule get2dRenderVert();
// ----------------------------------------------------------------------------
VkShaderModule get2dRenderFrag();
// ----------------------------------------------------------------------------
unsigned getSamplerSize();
}; // GEVulkanShaderManager
}